]> granicus.if.org Git - llvm/commitdiff
[SampleFDO] Add profile symbol list section to discriminate function being
authorWei Mi <wmi@google.com>
Sat, 31 Aug 2019 02:27:26 +0000 (02:27 +0000)
committerWei Mi <wmi@google.com>
Sat, 31 Aug 2019 02:27:26 +0000 (02:27 +0000)
cold versus function being newly added.

This is the second half of https://reviews.llvm.org/D66374.

Profile symbol list is the collection of function symbols showing up in
the binary which generates the current profile. It is used to discriminate
function being cold versus function being newly added. Profile symbol list
is only added for profile with ExtBinary format.

During profile use compilation, when profile-sample-accurate is enabled,
a function without profile will be regarded as cold only when it is
contained in that list.

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

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@370563 91177308-0d34-0410-b5e6-96231b3b80d8

17 files changed:
include/llvm/ProfileData/SampleProf.h
include/llvm/ProfileData/SampleProfReader.h
include/llvm/ProfileData/SampleProfWriter.h
lib/ProfileData/SampleProf.cpp
lib/ProfileData/SampleProfReader.cpp
lib/ProfileData/SampleProfWriter.cpp
lib/Transforms/IPO/SampleProfile.cpp
test/Transforms/SampleProfile/Inputs/profile-symbol-list.ll [new file with mode: 0644]
test/Transforms/SampleProfile/Inputs/profile-symbol-list.text [new file with mode: 0644]
test/Transforms/SampleProfile/compressed-profile-symbol-list.ll [new file with mode: 0644]
test/Transforms/SampleProfile/uncompressed-profile-symbol-list.ll [new file with mode: 0644]
test/tools/llvm-profdata/Inputs/profile-symbol-list-1.text [new file with mode: 0644]
test/tools/llvm-profdata/Inputs/profile-symbol-list-2.text [new file with mode: 0644]
test/tools/llvm-profdata/Inputs/profile-symbol-list.expected [new file with mode: 0644]
test/tools/llvm-profdata/profile-symbol-list.test [new file with mode: 0644]
tools/llvm-profdata/llvm-profdata.cpp
unittests/ProfileData/SampleProfTest.cpp

index 2e7bfa9d91e6ab218365995a57b9dca11c9d995a..e5cc494b92c3283027c3dd3d7386b40d5cb5f7fb 100644 (file)
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/IR/Function.h"
 #include "llvm/IR/GlobalValue.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/ErrorOr.h"
 #include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <cstdint>
 #include <map>
@@ -50,7 +52,10 @@ enum class sampleprof_error {
   truncated_name_table,
   not_implemented,
   counter_overflow,
-  ostream_seek_unsupported
+  ostream_seek_unsupported,
+  compress_failed,
+  uncompress_failed,
+  zlib_unavailable
 };
 
 inline std::error_code make_error_code(sampleprof_error E) {
@@ -114,6 +119,7 @@ enum SecType {
   SecInValid = 0,
   SecProfSummary = 1,
   SecNameTable = 2,
+  SecProfileSymbolList = 3,
   // marker for the first type of profile.
   SecFuncProfileFirst = 32,
   SecLBRProfile = SecFuncProfileFirst
@@ -595,6 +601,47 @@ private:
   SamplesWithLocList V;
 };
 
+/// ProfileSymbolList records the list of function symbols shown up
+/// in the binary used to generate the profile. It is useful to
+/// to discriminate a function being so cold as not to shown up
+/// in the profile and a function newly added.
+class ProfileSymbolList {
+public:
+  /// copy indicates whether we need to copy the underlying memory
+  /// for the input Name.
+  void add(StringRef Name, bool copy = false) {
+    if (!copy) {
+      Syms.insert(Name);
+      return;
+    }
+    Syms.insert(Name.copy(Allocator));
+  }
+
+  bool contains(StringRef Name) { return Syms.count(Name); }
+
+  void merge(const ProfileSymbolList &List) {
+    for (auto Sym : List.Syms)
+      add(Sym, true);
+  }
+
+  unsigned size() { return Syms.size(); }
+
+  void setToCompress(bool TC) { ToCompress = TC; }
+
+  std::error_code read(uint64_t CompressSize, uint64_t UncompressSize,
+                       const uint8_t *Data);
+  std::error_code write(raw_ostream &OS);
+  void dump(raw_ostream &OS = dbgs()) const;
+
+private:
+  // Determine whether or not to compress the symbol list when
+  // writing it into profile. The variable is unused when the symbol
+  // list is read from an existing profile.
+  bool ToCompress = true;
+  DenseSet<StringRef> Syms;
+  BumpPtrAllocator Allocator;
+};
+
 } // end namespace sampleprof
 } // end namespace llvm
 
index b6ce620e39d92d3512418fe1af25bb6a3ecb3e8e..d690c6d6edb815bd67a309bb05605b893dba1d22 100644 (file)
@@ -326,6 +326,10 @@ public:
   /// \brief Return the profile format.
   SampleProfileFormat getFormat() { return Format; }
 
+  virtual std::unique_ptr<ProfileSymbolList> getProfileSymbolList() {
+    return nullptr;
+  };
+
 protected:
   /// Map every function to its associated profile.
   ///
@@ -477,6 +481,7 @@ public:
 class SampleProfileReaderExtBinaryBase : public SampleProfileReaderBinary {
 protected:
   std::vector<SecHdrTableEntry> SecHdrTable;
+  std::unique_ptr<ProfileSymbolList> ProfSymList;
   std::error_code readSecHdrTableEntry();
   std::error_code readSecHdrTable();
   virtual std::error_code readHeader() override;
@@ -498,6 +503,7 @@ private:
   virtual std::error_code verifySPMagic(uint64_t Magic) override;
   virtual std::error_code readOneSection(const uint8_t *Start, uint64_t Size,
                                          SecType Type) override;
+  std::error_code readProfileSymbolList();
 
 public:
   SampleProfileReaderExtBinary(std::unique_ptr<MemoryBuffer> B, LLVMContext &C,
@@ -506,6 +512,10 @@ public:
 
   /// \brief Return true if \p Buffer is in the format supported by this class.
   static bool hasFormat(const MemoryBuffer &Buffer);
+
+  virtual std::unique_ptr<ProfileSymbolList> getProfileSymbolList() override {
+    return std::move(ProfSymList);
+  };
 };
 
 class SampleProfileReaderCompactBinary : public SampleProfileReaderBinary {
index 67df6d3cd0e19e73db405528004e557a7934699c..35218e3879c4a3fff94b089b99a977311bca2460 100644 (file)
@@ -56,6 +56,8 @@ public:
   static ErrorOr<std::unique_ptr<SampleProfileWriter>>
   create(std::unique_ptr<raw_ostream> &OS, SampleProfileFormat Format);
 
+  virtual void setProfileSymbolList(ProfileSymbolList *PSL) {}
+
 protected:
   SampleProfileWriter(std::unique_ptr<raw_ostream> &OS)
       : OutputStream(std::move(OS)) {}
@@ -175,12 +177,19 @@ private:
 class SampleProfileWriterExtBinary : public SampleProfileWriterExtBinaryBase {
   using SampleProfileWriterExtBinaryBase::SampleProfileWriterExtBinaryBase;
 
+public:
+  virtual void setProfileSymbolList(ProfileSymbolList *PSL) override {
+    ProfSymList = PSL;
+  };
+
 private:
   virtual void initSectionLayout() override {
-    SectionLayout = {SecProfSummary, SecNameTable, SecLBRProfile};
+    SectionLayout = {SecProfSummary, SecNameTable, SecLBRProfile,
+                     SecProfileSymbolList};
   };
   virtual std::error_code
   writeSections(const StringMap<FunctionSamples> &ProfileMap) override;
+  ProfileSymbolList *ProfSymList = nullptr;
 };
 
 // CompactBinary is a compact format of binary profile which both reduces
index ce0f537f8d54c13930556d7e80e655b99ff5b03f..36c39c12432038ef89d48c0009130a9f3270643b 100644 (file)
 #include "llvm/Config/llvm-config.h"
 #include "llvm/IR/DebugInfoMetadata.h"
 #include "llvm/Support/Compiler.h"
+#include "llvm/Support/Compression.h"
 #include "llvm/Support/Debug.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/LEB128.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/raw_ostream.h"
 #include <string>
@@ -66,6 +69,12 @@ class SampleProfErrorCategoryType : public std::error_category {
       return "Counter overflow";
     case sampleprof_error::ostream_seek_unsupported:
       return "Ostream does not support seek";
+    case sampleprof_error::compress_failed:
+      return "Compress failure";
+    case sampleprof_error::uncompress_failed:
+      return "Uncompress failure";
+    case sampleprof_error::zlib_unavailable:
+      return "Zlib is unavailable";
     }
     llvm_unreachable("A value of sampleprof_error has no message.");
   }
@@ -188,3 +197,75 @@ FunctionSamples::findFunctionSamples(const DILocation *DIL) const {
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
 LLVM_DUMP_METHOD void FunctionSamples::dump() const { print(dbgs(), 0); }
 #endif
+
+std::error_code ProfileSymbolList::read(uint64_t CompressSize,
+                                        uint64_t UncompressSize,
+                                        const uint8_t *Data) {
+  const char *ListStart = reinterpret_cast<const char *>(Data);
+  // CompressSize being non-zero means the profile is compressed and
+  // needs to be uncompressed first.
+  if (CompressSize) {
+    if (!llvm::zlib::isAvailable())
+      return sampleprof_error::zlib_unavailable;
+
+    StringRef CompressedStrings(reinterpret_cast<const char *>(Data),
+                                CompressSize);
+    char *Buffer = Allocator.Allocate<char>(UncompressSize);
+    llvm::Error E = zlib::uncompress(CompressedStrings, Buffer, UncompressSize);
+    if (E)
+      return sampleprof_error::uncompress_failed;
+    ListStart = Buffer;
+  }
+
+  uint64_t Size = 0;
+  while (Size < UncompressSize) {
+    StringRef Str(ListStart + Size);
+    add(Str);
+    Size += Str.size() + 1;
+  }
+  return sampleprof_error::success;
+}
+
+std::error_code ProfileSymbolList::write(raw_ostream &OS) {
+  // Sort the symbols before doing compression. It will make the
+  // compression much more effective.
+  std::vector<StringRef> SortedList;
+  SortedList.insert(SortedList.begin(), Syms.begin(), Syms.end());
+  llvm::sort(SortedList);
+
+  std::string UncompressedStrings;
+  for (auto &Sym : SortedList) {
+    UncompressedStrings.append(Sym.str());
+    UncompressedStrings.append(1, '\0');
+  }
+
+  if (ToCompress) {
+    if (!llvm::zlib::isAvailable())
+      return sampleprof_error::zlib_unavailable;
+    SmallString<128> CompressedStrings;
+    llvm::Error E = zlib::compress(UncompressedStrings, CompressedStrings,
+                                   zlib::BestSizeCompression);
+    if (E)
+      return sampleprof_error::compress_failed;
+    encodeULEB128(UncompressedStrings.size(), OS);
+    encodeULEB128(CompressedStrings.size(), OS);
+    OS << CompressedStrings.str();
+  } else {
+    encodeULEB128(UncompressedStrings.size(), OS);
+    // If profile symbol list is not compressed, we will still save
+    // a compressed size value, but the value of the size is 0.
+    encodeULEB128(0, OS);
+    OS << UncompressedStrings;
+  }
+  return sampleprof_error::success;
+}
+
+void ProfileSymbolList::dump(raw_ostream &OS) const {
+  OS << "======== Dump profile symbol list ========\n";
+  std::vector<StringRef> SortedList;
+  SortedList.insert(SortedList.begin(), Syms.begin(), Syms.end());
+  llvm::sort(SortedList);
+
+  for (auto &Sym : SortedList)
+    OS << Sym << "\n";
+}
index e309d5b270a96210487d483e0da83f959bd4e1c2..f6b33d962616c137582c56f11f6d5dfe1dddfaa6 100644 (file)
@@ -486,12 +486,40 @@ SampleProfileReaderExtBinary::readOneSection(const uint8_t *Start,
         return EC;
     }
     break;
+  case SecProfileSymbolList:
+    if (std::error_code EC = readProfileSymbolList())
+      return EC;
+    break;
   default:
     break;
   }
   return sampleprof_error::success;
 }
 
+std::error_code SampleProfileReaderExtBinary::readProfileSymbolList() {
+  auto UncompressSize = readNumber<uint64_t>();
+  if (std::error_code EC = UncompressSize.getError())
+    return EC;
+
+  auto CompressSize = readNumber<uint64_t>();
+  if (std::error_code EC = CompressSize.getError())
+    return EC;
+
+  if (!ProfSymList)
+    ProfSymList = std::make_unique<ProfileSymbolList>();
+
+  if (std::error_code EC =
+          ProfSymList->read(*CompressSize, *UncompressSize, Data))
+    return EC;
+
+  // CompressSize is zero only when ProfileSymbolList is not compressed.
+  if (*CompressSize == 0)
+    Data = Data + *UncompressSize;
+  else
+    Data = Data + *CompressSize;
+  return sampleprof_error::success;
+}
+
 std::error_code SampleProfileReaderExtBinaryBase::read() {
   const uint8_t *BufStart =
       reinterpret_cast<const uint8_t *>(Buffer->getBufferStart());
index 47f97de2a3b053569f8a5750c4e8057bcea5f6d5..068ce5bf959cda1ed7ad1cde942918a9f785958e 100644 (file)
@@ -121,7 +121,12 @@ std::error_code SampleProfileWriterExtBinary::writeSections(
 
   if (std::error_code EC = writeFuncProfiles(ProfileMap))
     return EC;
-  addNewSection(SecLBRProfile, SectionStart);
+  SectionStart = addNewSection(SecLBRProfile, SectionStart);
+
+  if (ProfSymList && ProfSymList->size() > 0)
+    if (std::error_code EC = ProfSymList->write(*OutputStream))
+      return EC;
+  addNewSection(SecProfileSymbolList, SectionStart);
 
   return sampleprof_error::success;
 }
index 224da4b5550a6bd5cd9b42f92c114184c4c32169..eb4ddfd179b96bbdcf2e57464c65d8a8a960b057 100644 (file)
@@ -379,6 +379,10 @@ protected:
   /// Profile Summary Info computed from sample profile.
   ProfileSummaryInfo *PSI = nullptr;
 
+  /// Profle Symbol list tells whether a function name appears in the binary
+  /// used to generate the current profile.
+  std::unique_ptr<ProfileSymbolList> PSL;
+
   /// Total number of samples collected in this profile.
   ///
   /// This is the sum of all the samples collected in all the functions executed
@@ -1634,6 +1638,7 @@ bool SampleProfileLoader::doInitialization(Module &M) {
   Reader = std::move(ReaderOrErr.get());
   Reader->collectFuncsToUse(M);
   ProfileIsValid = (Reader->read() == sampleprof_error::success);
+  PSL = Reader->getProfileSymbolList();
 
   if (!RemappingFilename.empty()) {
     // Apply profile remappings to the loaded profile data if requested.
@@ -1725,11 +1730,15 @@ bool SampleProfileLoader::runOnFunction(Function &F, ModuleAnalysisManager *AM)
   // conservatively by getEntryCount as the same as unknown (None). This is
   // to avoid newly added code to be treated as cold. If we have samples
   // this will be overwritten in emitAnnotations.
+  //
+  // PSL -- profile symbol list include all the symbols in sampled binary.
   // If ProfileSampleAccurate is true or F has profile-sample-accurate
-  // attribute, initialize the entry count to 0 so callsites or functions
-  // unsampled will be treated as cold.
+  // attribute, and if there is no profile symbol list read in, initialize
+  // all the function entry counts to 0; if there is profile symbol list, only
+  // initialize the entry count to 0 when current function is in the list.
   uint64_t initialEntryCount =
-      (ProfileSampleAccurate || F.hasFnAttribute("profile-sample-accurate"))
+      ((ProfileSampleAccurate || F.hasFnAttribute("profile-sample-accurate")) &&
+       (!PSL || PSL->contains(F.getName())))
           ? 0
           : -1;
   F.setEntryCount(ProfileCount(initialEntryCount, Function::PCT_Real));
diff --git a/test/Transforms/SampleProfile/Inputs/profile-symbol-list.ll b/test/Transforms/SampleProfile/Inputs/profile-symbol-list.ll
new file mode 100644 (file)
index 0000000..c4623fb
--- /dev/null
@@ -0,0 +1,134 @@
+; Original C++ test case
+;
+; #include <stdio.h>
+;
+; __attribute__((noinline)) int goo() { return 3 };
+; __attribute__((noinline)) int hoo() { return 4 };
+;
+; int sum(int x, int y) {
+;   return x + y;
+; }
+;
+; int main() {
+;   int s, i = 0;
+;   while (i++ < 20000 * 20000)
+;     if (i != 100) s = sum(i, s); else s = 30;
+;   printf("sum is %d\n", s);
+;   return goo() + hoo() != 7;
+; }
+;
+; Both goo and hoo don't show up in the input profile.
+; Suppose function goo shows up in the binary generating the input profile
+; and function hoo doesn't show up. Then the profile symbol list in the input
+; profile will contain goo but not hoo. Verify the entry count of goo is
+; 0 and the entry count of hoo is -1.
+; CHECK: define {{.*}} i32 @_Z3goov() {{.*}} !prof ![[IDX1:[0-9]*]]
+; CHECK: define {{.*}} i32 @_Z3hoov() {{.*}} !prof ![[IDX2:[0-9]*]]
+; CHECK: ![[IDX1]] = !{!"function_entry_count", i64 0}
+; CHECK: ![[IDX2]] = !{!"function_entry_count", i64 -1}
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+@.str = private unnamed_addr constant [11 x i8] c"sum is %d\0A\00", align 1
+
+; Function Attrs: noinline norecurse nounwind readnone uwtable
+define dso_local i32 @_Z3goov() local_unnamed_addr #0 !dbg !7 {
+entry:
+  ret i32 3, !dbg !9
+}
+
+; Function Attrs: noinline norecurse nounwind readnone uwtable
+define dso_local i32 @_Z3hoov() local_unnamed_addr #0 !dbg !10 {
+entry:
+  ret i32 4, !dbg !11
+}
+
+; Function Attrs: norecurse nounwind readnone uwtable
+define dso_local i32 @_Z3sumii(i32 %x, i32 %y) local_unnamed_addr #1 !dbg !12 {
+entry:
+  %add = add nsw i32 %y, %x, !dbg !13
+  ret i32 %add, !dbg !14
+}
+
+; Function Attrs: nofree norecurse nounwind uwtable
+define dso_local i32 @main() local_unnamed_addr #2 !dbg !15 {
+entry:
+  br label %while.body, !dbg !16
+
+while.body:                                       ; preds = %while.body, %entry
+  %inc12 = phi i32 [ 1, %entry ], [ %inc.4, %while.body ]
+  %s.011 = phi i32 [ undef, %entry ], [ %spec.select.4, %while.body ]
+  %cmp1 = icmp eq i32 %inc12, 100, !dbg !18
+  %add.i = add nsw i32 %inc12, %s.011, !dbg !20
+  %spec.select = select i1 %cmp1, i32 30, i32 %add.i, !dbg !23
+  %inc = add nuw nsw i32 %inc12, 1, !dbg !24
+  %cmp1.1 = icmp eq i32 %inc, 100, !dbg !18
+  %add.i.1 = add nsw i32 %inc, %spec.select, !dbg !20
+  %spec.select.1 = select i1 %cmp1.1, i32 30, i32 %add.i.1, !dbg !23
+  %inc.1 = add nuw nsw i32 %inc12, 2, !dbg !24
+  %cmp1.2 = icmp eq i32 %inc.1, 100, !dbg !18
+  %add.i.2 = add nsw i32 %inc.1, %spec.select.1, !dbg !20
+  %spec.select.2 = select i1 %cmp1.2, i32 30, i32 %add.i.2, !dbg !23
+  %inc.2 = add nuw nsw i32 %inc12, 3, !dbg !24
+  %cmp1.3 = icmp eq i32 %inc.2, 100, !dbg !18
+  %add.i.3 = add nsw i32 %inc.2, %spec.select.2, !dbg !20
+  %spec.select.3 = select i1 %cmp1.3, i32 30, i32 %add.i.3, !dbg !23
+  %inc.3 = add nuw nsw i32 %inc12, 4, !dbg !24
+  %cmp1.4 = icmp eq i32 %inc.3, 100, !dbg !18
+  %add.i.4 = add nsw i32 %inc.3, %spec.select.3, !dbg !20
+  %spec.select.4 = select i1 %cmp1.4, i32 30, i32 %add.i.4, !dbg !23
+  %inc.4 = add nuw nsw i32 %inc12, 5, !dbg !24
+  %exitcond.4 = icmp eq i32 %inc.4, 400000001, !dbg !26
+  br i1 %exitcond.4, label %while.end, label %while.body, !dbg !27, !llvm.loop !28
+
+while.end:                                        ; preds = %while.body
+  %call2 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str, i64 0, i64 0), i32 %spec.select.4), !dbg !31
+  ret i32 0, !dbg !32
+}
+
+; Function Attrs: nofree nounwind
+declare dso_local i32 @printf(i8* nocapture readonly, ...) local_unnamed_addr #3
+
+attributes #0 = { noinline norecurse nounwind readnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { norecurse nounwind readnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { nofree norecurse nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #3 = { nofree nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5}
+!llvm.ident = !{!6}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 10.0.0 (trunk 369144)", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, enums: !2, debugInfoForProfiling: true, nameTableKind: None)
+!1 = !DIFile(filename: "1.cc", directory: "/usr/local/google/home/wmi/workarea/llvm-r369144/src")
+!2 = !{}
+!3 = !{i32 2, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{!"clang version 10.0.0 (trunk 369144)"}
+!7 = distinct !DISubprogram(name: "goo", linkageName: "_Z3goov", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!8 = !DISubroutineType(types: !2)
+!9 = !DILocation(line: 3, column: 39, scope: !7)
+!10 = distinct !DISubprogram(name: "hoo", linkageName: "_Z3hoov", scope: !1, file: !1, line: 4, type: !8, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!11 = !DILocation(line: 4, column: 39, scope: !10)
+!12 = distinct !DISubprogram(name: "sum", linkageName: "_Z3sumii", scope: !1, file: !1, line: 6, type: !8, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!13 = !DILocation(line: 7, column: 12, scope: !12)
+!14 = !DILocation(line: 7, column: 3, scope: !12)
+!15 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 10, type: !8, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!16 = !DILocation(line: 12, column: 3, scope: !17)
+!17 = !DILexicalBlockFile(scope: !15, file: !1, discriminator: 2)
+!18 = !DILocation(line: 13, column: 11, scope: !19)
+!19 = !DILexicalBlockFile(scope: !15, file: !1, discriminator: 21)
+!20 = !DILocation(line: 7, column: 12, scope: !21, inlinedAt: !22)
+!21 = !DILexicalBlockFile(scope: !12, file: !1, discriminator: 21)
+!22 = distinct !DILocation(line: 13, column: 23, scope: !17)
+!23 = !DILocation(line: 13, column: 9, scope: !19)
+!24 = !DILocation(line: 12, column: 11, scope: !25)
+!25 = !DILexicalBlockFile(scope: !15, file: !1, discriminator: 1282)
+!26 = !DILocation(line: 12, column: 14, scope: !25)
+!27 = !DILocation(line: 12, column: 3, scope: !25)
+!28 = distinct !{!28, !29, !30}
+!29 = !DILocation(line: 12, column: 3, scope: !15)
+!30 = !DILocation(line: 13, column: 43, scope: !15)
+!31 = !DILocation(line: 14, column: 3, scope: !15)
+!32 = !DILocation(line: 15, column: 3, scope: !15)
diff --git a/test/Transforms/SampleProfile/Inputs/profile-symbol-list.text b/test/Transforms/SampleProfile/Inputs/profile-symbol-list.text
new file mode 100644 (file)
index 0000000..3763872
--- /dev/null
@@ -0,0 +1,9 @@
+_Z3goov
+_Z3sumii
+__libc_csu_fini
+__libc_csu_init
+_dl_relocate_static_pie
+_fini
+_init
+_start
+main
diff --git a/test/Transforms/SampleProfile/compressed-profile-symbol-list.ll b/test/Transforms/SampleProfile/compressed-profile-symbol-list.ll
new file mode 100644 (file)
index 0000000..dfd6621
--- /dev/null
@@ -0,0 +1,5 @@
+; REQUIRES: zlib
+; Append inline.prof with profile symbol list and save it after compression.
+; RUN: llvm-profdata merge --sample --prof-sym-list=%S/Inputs/profile-symbol-list.text --compress-prof-sym-list=true --extbinary %S/Inputs/inline.prof --output=%t.profdata
+; RUN: opt < %S/Inputs/profile-symbol-list.ll -sample-profile -profile-sample-accurate -sample-profile-file=%t.profdata -S | FileCheck %S/Inputs/profile-symbol-list.ll
+; RUN: opt < %S/Inputs/profile-symbol-list.ll -passes=sample-profile -profile-sample-accurate -sample-profile-file=%t.profdata -S | FileCheck %S/Inputs/profile-symbol-list.ll
diff --git a/test/Transforms/SampleProfile/uncompressed-profile-symbol-list.ll b/test/Transforms/SampleProfile/uncompressed-profile-symbol-list.ll
new file mode 100644 (file)
index 0000000..ed73765
--- /dev/null
@@ -0,0 +1,4 @@
+; Append inline.prof with profile symbol list and save it without compression.
+; RUN: llvm-profdata merge --sample --prof-sym-list=%S/Inputs/profile-symbol-list.text --compress-prof-sym-list=false --extbinary %S/Inputs/inline.prof --output=%t.profdata
+; RUN: opt < %S/Inputs/profile-symbol-list.ll -sample-profile -profile-sample-accurate -sample-profile-file=%t.profdata -S | FileCheck %S/Inputs/profile-symbol-list.ll
+; RUN: opt < %S/Inputs/profile-symbol-list.ll -passes=sample-profile -profile-sample-accurate -sample-profile-file=%t.profdata -S | FileCheck %S/Inputs/profile-symbol-list.ll
diff --git a/test/tools/llvm-profdata/Inputs/profile-symbol-list-1.text b/test/tools/llvm-profdata/Inputs/profile-symbol-list-1.text
new file mode 100644 (file)
index 0000000..19d1ce4
--- /dev/null
@@ -0,0 +1,5 @@
+_Z3goov
+_Z3sumii
+__libc_csu_fini
+__libc_csu_init
+_dl_relocate_static_pie
diff --git a/test/tools/llvm-profdata/Inputs/profile-symbol-list-2.text b/test/tools/llvm-profdata/Inputs/profile-symbol-list-2.text
new file mode 100644 (file)
index 0000000..02fc36a
--- /dev/null
@@ -0,0 +1,4 @@
+_fini
+_init
+_start
+main
diff --git a/test/tools/llvm-profdata/Inputs/profile-symbol-list.expected b/test/tools/llvm-profdata/Inputs/profile-symbol-list.expected
new file mode 100644 (file)
index 0000000..d9af0de
--- /dev/null
@@ -0,0 +1,42 @@
+Function: main: 368038, 0, 7 sampled lines
+Samples collected in the function's body {
+  4: 1068
+  4.2: 1068
+  5: 2150
+  5.1: 2150
+  6: 4160
+  7: 1068
+  9: 4128, calls: _Z3bari:2942 _Z3fooi:1262
+}
+Samples collected in inlined callsites {
+  10: inlined callee: inline1: 2000, 0, 1 sampled lines
+    Samples collected in the function's body {
+      1: 2000
+    }
+    No inlined callsites in this function
+  10: inlined callee: inline2: 4000, 0, 1 sampled lines
+    Samples collected in the function's body {
+      1: 4000
+    }
+    No inlined callsites in this function
+}
+Function: _Z3fooi: 15422, 1220, 1 sampled lines
+Samples collected in the function's body {
+  1: 1220
+}
+No inlined callsites in this function
+Function: _Z3bari: 40602, 2874, 1 sampled lines
+Samples collected in the function's body {
+  1: 2874
+}
+No inlined callsites in this function
+======== Dump profile symbol list ========
+_Z3goov
+_Z3sumii
+__libc_csu_fini
+__libc_csu_init
+_dl_relocate_static_pie
+_fini
+_init
+_start
+main
diff --git a/test/tools/llvm-profdata/profile-symbol-list.test b/test/tools/llvm-profdata/profile-symbol-list.test
new file mode 100644 (file)
index 0000000..fc24a92
--- /dev/null
@@ -0,0 +1,5 @@
+; RUN: llvm-profdata merge -sample -extbinary -prof-sym-list=%S/Inputs/profile-symbol-list-1.text %S/Inputs/sample-profile.proftext -o %t.1.output
+; RUN: llvm-profdata merge -sample -extbinary -prof-sym-list=%S/Inputs/profile-symbol-list-2.text %S/Inputs/sample-profile.proftext -o %t.2.output
+; RUN: llvm-profdata merge -sample -extbinary %t.1.output %t.2.output -o %t.3.output
+; RUN: llvm-profdata show -sample -show-prof-sym-list %t.3.output > %t.4.output
+; RUN: diff %S/Inputs/profile-symbol-list.expected %t.4.output
index e185c43736146a584dca79f7d3c01849f7a77752..d3b7c2b2c800d0879c347bfbfa9a9f15824d1a8f 100644 (file)
@@ -433,14 +433,40 @@ static sampleprof::SampleProfileFormat FormatMap[] = {
     sampleprof::SPF_GCC,
     sampleprof::SPF_Binary};
 
-static void mergeSampleProfile(const WeightedFileVector &Inputs,
-                               SymbolRemapper *Remapper,
-                               StringRef OutputFilename,
-                               ProfileFormat OutputFormat) {
+static std::unique_ptr<MemoryBuffer>
+getInputFileBuf(const StringRef &InputFile) {
+  if (InputFile == "")
+    return {};
+
+  auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
+  if (!BufOrError)
+    exitWithErrorCode(BufOrError.getError(), InputFile);
+
+  return std::move(*BufOrError);
+}
+
+static void populateProfileSymbolList(MemoryBuffer *Buffer,
+                                      sampleprof::ProfileSymbolList &PSL) {
+  if (!Buffer)
+    return;
+
+  SmallVector<StringRef, 32> SymbolVec;
+  StringRef Data = Buffer->getBuffer();
+  Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
+
+  for (StringRef symbol : SymbolVec)
+    PSL.add(symbol);
+}
+
+static void
+mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper,
+                   StringRef OutputFilename, ProfileFormat OutputFormat,
+                   StringRef ProfileSymbolListFile, bool CompressProfSymList) {
   using namespace sampleprof;
   StringMap<FunctionSamples> ProfileMap;
   SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
   LLVMContext Context;
+  sampleprof::ProfileSymbolList WriterList;
   for (const auto &Input : Inputs) {
     auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context);
     if (std::error_code EC = ReaderOrErr.getError())
@@ -471,13 +497,28 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs,
         handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName);
       }
     }
+
+    std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
+        Reader->getProfileSymbolList();
+    if (ReaderList)
+      WriterList.merge(*ReaderList);
   }
   auto WriterOrErr =
       SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
   if (std::error_code EC = WriterOrErr.getError())
     exitWithErrorCode(EC, OutputFilename);
 
+  // WriterList will have StringRef refering to string in Buffer.
+  // Make sure Buffer lives as long as WriterList.
+  auto Buffer = getInputFileBuf(ProfileSymbolListFile);
+  populateProfileSymbolList(Buffer.get(), WriterList);
+  WriterList.setToCompress(CompressProfSymList);
+  if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary)
+    warn("Profile Symbol list is not empty but the output format is not "
+         "ExtBinary format. The list will be lost in the output. ");
+
   auto Writer = std::move(WriterOrErr.get());
+  Writer->setProfileSymbolList(&WriterList);
   Writer->write(ProfileMap);
 }
 
@@ -492,18 +533,6 @@ static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
   return {FileName, Weight};
 }
 
-static std::unique_ptr<MemoryBuffer>
-getInputFilenamesFileBuf(const StringRef &InputFilenamesFile) {
-  if (InputFilenamesFile == "")
-    return {};
-
-  auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFilenamesFile);
-  if (!BufOrError)
-    exitWithErrorCode(BufOrError.getError(), InputFilenamesFile);
-
-  return std::move(*BufOrError);
-}
-
 static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {
   StringRef Filename = WF.Filename;
   uint64_t Weight = WF.Weight;
@@ -603,6 +632,13 @@ static int merge_main(int argc, const char *argv[]) {
       cl::desc("Number of merge threads to use (default: autodetect)"));
   cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
                         cl::aliasopt(NumThreads));
+  cl::opt<std::string> ProfileSymbolListFile(
+      "prof-sym-list", cl::init(""),
+      cl::desc("Path to file containing the list of function symbols "
+               "used to populate profile symbol list"));
+  cl::opt<bool> CompressProfSymList(
+      "compress-prof-sym-list", cl::init(true), cl::Hidden,
+      cl::desc("Compress profile symbol list before write it into profile. "));
 
   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
 
@@ -614,7 +650,7 @@ static int merge_main(int argc, const char *argv[]) {
 
   // Make sure that the file buffer stays alive for the duration of the
   // weighted input vector's lifetime.
-  auto Buffer = getInputFilenamesFileBuf(InputFilenamesFile);
+  auto Buffer = getInputFileBuf(InputFilenamesFile);
   parseInputFilenamesFile(Buffer.get(), WeightedInputs);
 
   if (WeightedInputs.empty())
@@ -636,7 +672,8 @@ static int merge_main(int argc, const char *argv[]) {
                       OutputFormat, OutputSparse, NumThreads);
   else
     mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename,
-                       OutputFormat);
+                       OutputFormat, ProfileSymbolListFile,
+                       CompressProfSymList);
 
   return 0;
 }
@@ -954,7 +991,7 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts,
 static int showSampleProfile(const std::string &Filename, bool ShowCounts,
                              bool ShowAllFunctions,
                              const std::string &ShowFunction,
-                             raw_fd_ostream &OS) {
+                             bool ShowProfileSymbolList, raw_fd_ostream &OS) {
   using namespace sampleprof;
   LLVMContext Context;
   auto ReaderOrErr = SampleProfileReader::create(Filename, Context);
@@ -970,6 +1007,12 @@ static int showSampleProfile(const std::string &Filename, bool ShowCounts,
   else
     Reader->dumpFunctionProfile(ShowFunction, OS);
 
+  if (ShowProfileSymbolList) {
+    std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
+        Reader->getProfileSymbolList();
+    ReaderList->dump(OS);
+  }
+
   return 0;
 }
 
@@ -1022,6 +1065,10 @@ static int show_main(int argc, const char *argv[]) {
       "list-below-cutoff", cl::init(false),
       cl::desc("Only output names of functions whose max count values are "
                "below the cutoff value"));
+  cl::opt<bool> ShowProfileSymbolList(
+      "show-prof-sym-list", cl::init(false),
+      cl::desc("Show profile symbol list if it exists in the profile. "));
+
   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n");
 
   if (OutputFilename.empty())
@@ -1049,7 +1096,7 @@ static int show_main(int argc, const char *argv[]) {
                             OnlyListBelow, ShowFunction, TextFormat, OS);
   else
     return showSampleProfile(Filename, ShowCounts, ShowAllFunctions,
-                             ShowFunction, OS);
+                             ShowFunction, ShowProfileSymbolList, OS);
 }
 
 int main(int argc, const char *argv[]) {
index ebf66b22c5babcb17ce68068dbdaadf4a5be437d..dd5ded5bc9b2fac1befb430a62a0560c4c1f0924 100644 (file)
@@ -96,6 +96,13 @@ struct SampleProfTest : ::testing::Test {
     Profiles[FooName] = std::move(FooSamples);
     Profiles[BarName] = std::move(BarSamples);
 
+    ProfileSymbolList List;
+    if (Format == SampleProfileFormat::SPF_Ext_Binary) {
+      List.add("zoo", true);
+      List.add("moo", true);
+    }
+    Writer->setProfileSymbolList(&List);
+
     std::error_code EC;
     EC = Writer->write(Profiles);
     ASSERT_TRUE(NoError(EC));
@@ -107,6 +114,13 @@ struct SampleProfTest : ::testing::Test {
     EC = Reader->read();
     ASSERT_TRUE(NoError(EC));
 
+    if (Format == SampleProfileFormat::SPF_Ext_Binary) {
+      std::unique_ptr<ProfileSymbolList> ReaderList =
+          Reader->getProfileSymbolList();
+      ReaderList->contains("zoo");
+      ReaderList->contains("moo");
+    }
+
     if (Remap) {
       auto MemBuffer = llvm::MemoryBuffer::getMemBuffer(R"(
         # Types 'int' and 'long' are equivalent