[llvm] 28277e9 - [AutoFDO][llvm-profgen] Report zero count for unexecuted part of function code

via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 24 14:16:22 PDT 2021


Author: wlei
Date: 2021-09-24T14:15:05-07:00
New Revision: 28277e9b48601dee7027ba6ed50554db821ba09b

URL: https://github.com/llvm/llvm-project/commit/28277e9b48601dee7027ba6ed50554db821ba09b
DIFF: https://github.com/llvm/llvm-project/commit/28277e9b48601dee7027ba6ed50554db821ba09b.diff

LOG: [AutoFDO][llvm-profgen] Report zero count for unexecuted part of function code

In order to be consistent with compiler that interprets zero count as unexecuted(cold), this change reports zero-value count for unexecuted part of function code. For the implementation, it leverages the range counter, initializes all the executed function range with the zero-value. After all ranges are merged and converted into disjoint ranges, the remaining zero count will indicates the unexecuted(cold) part of the function.

This change also extends the current `findDisjointRanges` method which now can support adding zero-value range.

Reviewed By: hoy, wenlei

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

Added: 
    

Modified: 
    llvm/test/tools/llvm-profgen/inline-noprobe.test
    llvm/test/tools/llvm-profgen/inline-noprobe2.test
    llvm/tools/llvm-profgen/ProfileGenerator.cpp
    llvm/tools/llvm-profgen/ProfileGenerator.h
    llvm/tools/llvm-profgen/ProfiledBinary.cpp
    llvm/tools/llvm-profgen/ProfiledBinary.h

Removed: 
    


################################################################################
diff  --git a/llvm/test/tools/llvm-profgen/inline-noprobe.test b/llvm/test/tools/llvm-profgen/inline-noprobe.test
index 39568aa7e8df..ef3c902730ff 100644
--- a/llvm/test/tools/llvm-profgen/inline-noprobe.test
+++ b/llvm/test/tools/llvm-profgen/inline-noprobe.test
@@ -4,10 +4,13 @@
 ; RUN: FileCheck %s --input-file %t --check-prefix=CHECK
 
 CHECK: main:836:0
+CHECK:  0: 0
+CHECK:  2: 0
 CHECK:  1: foo:836
 CHECK:   2.1: 42
 CHECK:   3: 62
 CHECK:   3.2: 21
+CHECK:   4: 0
 CHECK:   3.1: bar:252
 CHECK:    1: 42
 CHECK:   3.2: bar:63

diff  --git a/llvm/test/tools/llvm-profgen/inline-noprobe2.test b/llvm/test/tools/llvm-profgen/inline-noprobe2.test
index 9b4e643dcce7..9ae27d1a80ac 100644
--- a/llvm/test/tools/llvm-profgen/inline-noprobe2.test
+++ b/llvm/test/tools/llvm-profgen/inline-noprobe2.test
@@ -47,12 +47,20 @@
 ;CHECK-NEXT:   2: 5
 ;CHECK-NEXT:   3: 5
 ;CHECK-NEXT: main:213:0
+;CHECK-NEXT:  0: 0
+;CHECK-NEXT:  3: 0
+;CHECK-NEXT:  4.1: 0
+;CHECK-NEXT:  4.3: 0
 ;CHECK-NEXT:  5.1: 10
 ;CHECK-NEXT:  5.3: 10
 ;CHECK-NEXT:  6: 10
 ;CHECK-NEXT:  6.1: 12
 ;CHECK-NEXT:  6.3: 10
+;CHECK-NEXT:  7: 0
 ;CHECK-NEXT:  8: 0 quick_sort:1
+;CHECK-NEXT:  9: 0
+;CHECK-NEXT:  11: 0
+;CHECK-NEXT:  14: 0
 
 ; original code:
 ; clang -O3 -g -fno-optimize-sibling-calls -fdebug-info-for-profiling qsort.c -o a.out

diff  --git a/llvm/tools/llvm-profgen/ProfileGenerator.cpp b/llvm/tools/llvm-profgen/ProfileGenerator.cpp
index a71282a68bf5..77aff9d9967a 100644
--- a/llvm/tools/llvm-profgen/ProfileGenerator.cpp
+++ b/llvm/tools/llvm-profgen/ProfileGenerator.cpp
@@ -137,15 +137,25 @@ void ProfileGeneratorBase::findDisjointRanges(RangeSample &DisjointRanges,
   */
   struct BoundaryPoint {
     // Sum of sample counts beginning at this point
-    uint64_t BeginCount;
+    uint64_t BeginCount = UINT64_MAX;
     // Sum of sample counts ending at this point
-    uint64_t EndCount;
-
-    BoundaryPoint() : BeginCount(0), EndCount(0){};
-
-    void addBeginCount(uint64_t Count) { BeginCount += Count; }
+    uint64_t EndCount = UINT64_MAX;
+    // Is the begin point of a zero range.
+    bool IsZeroRangeBegin = false;
+    // Is the end point of a zero range.
+    bool IsZeroRangeEnd = false;
+
+    void addBeginCount(uint64_t Count) {
+      if (BeginCount == UINT64_MAX)
+        BeginCount = 0;
+      BeginCount += Count;
+    }
 
-    void addEndCount(uint64_t Count) { EndCount += Count; }
+    void addEndCount(uint64_t Count) {
+      if (EndCount == UINT64_MAX)
+        EndCount = 0;
+      EndCount += Count;
+    }
   };
 
   /*
@@ -174,41 +184,70 @@ void ProfileGeneratorBase::findDisjointRanges(RangeSample &DisjointRanges,
   [A, B-1]: 100
   [B, B]:   300
   [B+1, C]: 200.
+
+  Example for zero value range:
+
+    |<--- 100 --->|
+                       |<--- 200 --->|
+  |<---------------  0 ----------------->|
+  A  B            C    D             E   F
+
+  [A, B-1]  : 0
+  [B, C]    : 100
+  [C+1, D-1]: 0
+  [D, E]    : 200
+  [E+1, F]  : 0
   */
   std::map<uint64_t, BoundaryPoint> Boundaries;
 
   for (auto Item : Ranges) {
-    uint64_t Begin = Item.first.first;
-    uint64_t End = Item.first.second;
-    assert(Begin <= End && "Invalid instruction range");
+    assert(Item.first.first <= Item.first.second &&
+           "Invalid instruction range");
+    auto &BeginPoint = Boundaries[Item.first.first];
+    auto &EndPoint = Boundaries[Item.first.second];
     uint64_t Count = Item.second;
-    if (Boundaries.find(Begin) == Boundaries.end())
-      Boundaries[Begin] = BoundaryPoint();
-    Boundaries[Begin].addBeginCount(Count);
 
-    if (Boundaries.find(End) == Boundaries.end())
-      Boundaries[End] = BoundaryPoint();
-    Boundaries[End].addEndCount(Count);
+    BeginPoint.addBeginCount(Count);
+    EndPoint.addEndCount(Count);
+    if (Count == 0) {
+      BeginPoint.IsZeroRangeBegin = true;
+      EndPoint.IsZeroRangeEnd = true;
+    }
   }
 
+  // Use UINT64_MAX to indicate there is no existing range between BeginAddress
+  // and the next valid address
   uint64_t BeginAddress = UINT64_MAX;
+  int ZeroRangeDepth = 0;
   uint64_t Count = 0;
   for (auto Item : Boundaries) {
     uint64_t Address = Item.first;
     BoundaryPoint &Point = Item.second;
-    if (Point.BeginCount) {
+    if (Point.BeginCount != UINT64_MAX) {
       if (BeginAddress != UINT64_MAX)
         DisjointRanges[{BeginAddress, Address - 1}] = Count;
       Count += Point.BeginCount;
       BeginAddress = Address;
+      ZeroRangeDepth += Point.IsZeroRangeBegin;
     }
-    if (Point.EndCount) {
+    if (Point.EndCount != UINT64_MAX) {
       assert((BeginAddress != UINT64_MAX) &&
              "First boundary point cannot be 'end' point");
       DisjointRanges[{BeginAddress, Address}] = Count;
       assert(Count >= Point.EndCount && "Mismatched live ranges");
       Count -= Point.EndCount;
       BeginAddress = Address + 1;
+      ZeroRangeDepth -= Point.IsZeroRangeEnd;
+      // If the remaining count is zero and it's no longer in a zero range, this
+      // means we consume all the ranges before, thus mark BeginAddress as
+      // UINT64_MAX. e.g. supposing we have two non-overlapping ranges:
+      //  [<---- 10 ---->]
+      //                       [<---- 20 ---->]
+      //   A             B     C              D
+      // The BeginAddress(B+1) will reset to invalid(UINT64_MAX), so we won't
+      // have the [B+1, C-1] zero range.
+      if (Count == 0 && ZeroRangeDepth == 0)
+        BeginAddress = UINT64_MAX;
     }
   }
 }
@@ -223,7 +262,7 @@ void ProfileGeneratorBase::updateBodySamplesforFunctionProfile(
   ErrorOr<uint64_t> R = FunctionProfile.findSamplesAt(
       LeafLoc.Callsite.LineOffset, LeafLoc.Callsite.Discriminator);
   uint64_t PreviousCount = R ? R.get() : 0;
-  if (PreviousCount < Count) {
+  if (PreviousCount <= Count) {
     FunctionProfile.addBodySamples(LeafLoc.Callsite.LineOffset,
                                    LeafLoc.Callsite.Discriminator,
                                    Count - PreviousCount);
@@ -282,18 +321,41 @@ FunctionSamples &ProfileGenerator::getLeafProfileAndAddTotalSamples(
   return *FunctionProfile;
 }
 
+RangeSample
+ProfileGenerator::preprocessRangeCounter(const RangeSample &RangeCounter) {
+  RangeSample Ranges(RangeCounter.begin(), RangeCounter.end());
+  // For each range, we search for the range of the function it belongs to and
+  // initialize it with zero count, so it remains zero if doesn't hit any
+  // samples. This is to be consistent with compiler that interpret zero count
+  // as unexecuted(cold).
+  for (auto I : RangeCounter) {
+    uint64_t RangeBegin = I.first.first;
+    uint64_t RangeEnd = I.first.second;
+    // Find the function offset range the current range begin belongs to.
+    auto FuncRange = Binary->findFuncOffsetRange(RangeBegin);
+    if (FuncRange.second == 0)
+      WithColor::warning()
+          << "[" << format("%8" PRIx64, RangeBegin) << " - "
+          << format("%8" PRIx64, RangeEnd)
+          << "]: Invalid range or disassembling error in profiled binary.\n";
+    else if (RangeEnd > FuncRange.second)
+      WithColor::warning() << "[" << format("%8" PRIx64, RangeBegin) << " - "
+                           << format("%8" PRIx64, RangeEnd)
+                           << "]: Range is across 
diff erent functions.\n";
+    else
+      Ranges[FuncRange] += 0;
+  }
+  RangeSample DisjointRanges;
+  findDisjointRanges(DisjointRanges, Ranges);
+  return DisjointRanges;
+}
+
 void ProfileGenerator::populateBodySamplesForAllFunctions(
     const RangeSample &RangeCounter) {
-  RangeSample Ranges;
-  findDisjointRanges(Ranges, RangeCounter);
-  for (auto Range : Ranges) {
+  for (auto Range : preprocessRangeCounter(RangeCounter)) {
     uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first);
     uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second);
     uint64_t Count = Range.second;
-    // Disjoint ranges have introduce zero-filled gap that
-    // doesn't belong to current context, filter them out.
-    if (Count == 0)
-      continue;
 
     InstructionPointer IP(Binary, RangeBegin, true);
     // Disjoint ranges may have range in the middle of two instr,

diff  --git a/llvm/tools/llvm-profgen/ProfileGenerator.h b/llvm/tools/llvm-profgen/ProfileGenerator.h
index 681eac4d3dc6..64f469be363f 100644
--- a/llvm/tools/llvm-profgen/ProfileGenerator.h
+++ b/llvm/tools/llvm-profgen/ProfileGenerator.h
@@ -83,6 +83,7 @@ class ProfileGenerator : public ProfileGeneratorBase {
 
 private:
   void generateLineNumBasedProfile();
+  RangeSample preprocessRangeCounter(const RangeSample &RangeCounter);
   FunctionSamples &getTopLevelFunctionProfile(StringRef FuncName);
   // Helper function to get the leaf frame's FunctionProfile by traversing the
   // inline stack and meanwhile it adds the total samples for each frame's

diff  --git a/llvm/tools/llvm-profgen/ProfiledBinary.cpp b/llvm/tools/llvm-profgen/ProfiledBinary.cpp
index 3a1c5fa62c19..b04150842fb0 100644
--- a/llvm/tools/llvm-profgen/ProfiledBinary.cpp
+++ b/llvm/tools/llvm-profgen/ProfiledBinary.cpp
@@ -178,7 +178,7 @@ void ProfiledBinary::load() {
     FuncSizeTracker.trackInlineesOptimizedAway(ProbeDecoder);
 
   // Use function start and return address to infer prolog and epilog
-  ProEpilogTracker.inferPrologOffsets(FuncStartAddrMap);
+  ProEpilogTracker.inferPrologOffsets(FuncStartOffsetMap);
   ProEpilogTracker.inferEpilogOffsets(RetAddrs);
 
   // TODO: decode other sections.
@@ -397,7 +397,8 @@ bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
   if (ShowDisassembly)
     outs() << "\n";
 
-  FuncStartAddrMap[StartOffset] = Symbols[SI].Name.str();
+  FuncStartOffsetMap.emplace(StartOffset,
+                             std::make_pair(Symbols[SI].Name.str(), EndOffset));
   return true;
 }
 

diff  --git a/llvm/tools/llvm-profgen/ProfiledBinary.h b/llvm/tools/llvm-profgen/ProfiledBinary.h
index 85f079106187..01962d9d38e5 100644
--- a/llvm/tools/llvm-profgen/ProfiledBinary.h
+++ b/llvm/tools/llvm-profgen/ProfiledBinary.h
@@ -31,6 +31,7 @@
 #include "llvm/Support/Path.h"
 #include "llvm/Transforms/IPO/SampleContextTracker.h"
 #include <list>
+#include <map>
 #include <set>
 #include <sstream>
 #include <string>
@@ -78,9 +79,9 @@ struct PrologEpilogTracker {
   PrologEpilogTracker(ProfiledBinary *Bin) : Binary(Bin){};
 
   // Take the two addresses from the start of function as prolog
-  void inferPrologOffsets(
-      std::unordered_map<uint64_t, std::string> &FuncStartAddrMap) {
-    for (auto I : FuncStartAddrMap) {
+  void inferPrologOffsets(std::map<uint64_t, std::pair<std::string, uint64_t>>
+                              &FuncStartOffsetMap) {
+    for (auto I : FuncStartOffsetMap) {
       PrologEpilogSet.insert(I.first);
       InstructionPointer IP(Binary, I.first);
       IP.advance();
@@ -138,6 +139,8 @@ class BinarySizeContextTracker {
   ContextTrieNode RootContext;
 };
 
+using OffsetRange = std::pair<uint64_t, uint64_t>;
+
 class ProfiledBinary {
   // Absolute path of the binary.
   std::string Path;
@@ -161,8 +164,9 @@ class ProfiledBinary {
   // A list of text sections sorted by start RVA and size. Used to check
   // if a given RVA is a valid code address.
   std::set<std::pair<uint64_t, uint64_t>> TextSections;
-  // Function offset to name mapping.
-  std::unordered_map<uint64_t, std::string> FuncStartAddrMap;
+  // An ordered map of mapping function's start offset to its name and
+  // end offset.
+  std::map<uint64_t, std::pair<std::string, uint64_t>> FuncStartOffsetMap;
   // Offset to context location map. Used to expand the context.
   std::unordered_map<uint64_t, SampleContextFrameVector> Offset2LocStackMap;
   // An array of offsets of all instructions sorted in increasing order. The
@@ -304,10 +308,18 @@ class ProfiledBinary {
   }
 
   StringRef getFuncFromStartOffset(uint64_t Offset) {
-    auto I = FuncStartAddrMap.find(Offset);
-    if (I == FuncStartAddrMap.end())
+    auto I = FuncStartOffsetMap.find(Offset);
+    if (I == FuncStartOffsetMap.end())
       return StringRef();
-    return I->second;
+    return I->second.first;
+  }
+
+  OffsetRange findFuncOffsetRange(uint64_t Offset) {
+    auto I = FuncStartOffsetMap.upper_bound(Offset);
+    if (I == FuncStartOffsetMap.begin())
+      return {0, 0};
+    I--;
+    return {I->first, I->second.second};
   }
 
   uint32_t getFuncSizeForContext(SampleContext &Context) {


        


More information about the llvm-commits mailing list