]> granicus.if.org Git - clang/commitdiff
PGO: instrumentation based profiling sets function attributes.
authorManman Ren <manman.ren@gmail.com>
Wed, 5 Feb 2014 20:40:15 +0000 (20:40 +0000)
committerManman Ren <manman.ren@gmail.com>
Wed, 5 Feb 2014 20:40:15 +0000 (20:40 +0000)
We collect a maximal function count among all functions in the pgo data file.
For functions that are hot, we set its InlineHint attribute. For functions that
are cold, we set its Cold attribute.

We currently treat functions with >= 30% of the maximal function count as hot
and functions with <= 1% of the maximal function count are treated as cold.
These two numbers are from preliminary tuning on SPEC.

This commit should not affect non-PGO builds and should boost performance on
instrumentation based PGO.

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

lib/CodeGen/CodeGenFunction.cpp
lib/CodeGen/CodeGenPGO.cpp
lib/CodeGen/CodeGenPGO.h
test/CodeGen/Inputs/instr-attribute.pgodata [new file with mode: 0644]
test/CodeGen/instr-attribute.c [new file with mode: 0644]

index 7d451e8f6d00da815634bbadcc14287942b94a71..c0b4d8926a92a0f800d74dbbff0f2f509aad6fde 100644 (file)
@@ -591,6 +591,16 @@ void CodeGenFunction::StartFunction(GlobalDecl GD,
     EmitMCountInstrumentation();
 
   PGO.assignRegionCounters(GD);
+  if (CGM.getPGOData()) {
+    if (const Decl *D = GD.getDecl()) {
+      // Turn on InlineHint attribute for hot functions.
+      if (CGM.getPGOData()->isHotFunction(CGM.getMangledName(GD)))
+        Fn->addFnAttr(llvm::Attribute::InlineHint);
+      // Turn on Cold attribute for cold functions.
+      else if (CGM.getPGOData()->isColdFunction(CGM.getMangledName(GD)))
+        Fn->addFnAttr(llvm::Attribute::Cold);
+    }
+  }
 
   if (RetTy->isVoidType()) {
     // Void type; nothing to return.
index a1782d32b445e9053492e270b01781db9788c30f..d7c4709612aac2c8dd3a735877b5994936979a45 100644 (file)
@@ -45,6 +45,7 @@ PGOProfileData::PGOProfileData(CodeGenModule &CGM, std::string Path)
   const char *BufferStart = DataBuffer->getBufferStart();
   const char *BufferEnd = DataBuffer->getBufferEnd();
   const char *CurPtr = BufferStart;
+  uint64_t MaxCount = 0;
   while (CurPtr < BufferEnd) {
     // Read the mangled function name.
     const char *FuncName = CurPtr;
@@ -65,8 +66,19 @@ PGOProfileData::PGOProfileData(CodeGenModule &CGM, std::string Path)
     }
     CurPtr = EndPtr;
 
+    // Read function count.
+    uint64_t Count = strtoll(CurPtr, &EndPtr, 10);
+    if (EndPtr == CurPtr || *EndPtr != '\n') {
+      ReportBadPGOData(CGM, "pgo-data file has bad count value");
+      return;
+    }
+    CurPtr = EndPtr + 1;
+    FunctionCounts[MangledName] = Count;
+    MaxCount = Count > MaxCount ? Count : MaxCount;
+
     // There is one line for each counter; skip over those lines.
-    for (unsigned N = 0; N < NumCounters; ++N) {
+    // Since function count is already read, we start the loop from 1.
+    for (unsigned N = 1; N < NumCounters; ++N) {
       CurPtr = strchr(++CurPtr, '\n');
       if (!CurPtr) {
         ReportBadPGOData(CGM, "pgo data file is missing some counter info");
@@ -79,6 +91,33 @@ PGOProfileData::PGOProfileData(CodeGenModule &CGM, std::string Path)
 
     DataOffsets[MangledName] = FuncName - BufferStart;
   }
+  MaxFunctionCount = MaxCount;
+}
+
+/// Return true if a function is hot. If we know nothing about the function,
+/// return false.
+bool PGOProfileData::isHotFunction(StringRef MangledName) {
+  llvm::StringMap<uint64_t>::const_iterator CountIter =
+    FunctionCounts.find(MangledName);
+  // If we know nothing about the function, return false.
+  if (CountIter == FunctionCounts.end())
+    return false;
+  // FIXME: functions with >= 30% of the maximal function count are
+  // treated as hot. This number is from preliminary tuning on SPEC.
+  return CountIter->getValue() >= (uint64_t)(0.3 * (double)MaxFunctionCount);
+}
+
+/// Return true if a function is cold. If we know nothing about the function,
+/// return false.
+bool PGOProfileData::isColdFunction(StringRef MangledName) {
+  llvm::StringMap<uint64_t>::const_iterator CountIter =
+    FunctionCounts.find(MangledName);
+  // If we know nothing about the function, return false.
+  if (CountIter == FunctionCounts.end())
+    return false;
+  // FIXME: functions with <= 1% of the maximal function count are treated as
+  // cold. This number is from preliminary tuning on SPEC.
+  return CountIter->getValue() <= (uint64_t)(0.01 * (double)MaxFunctionCount);
 }
 
 bool PGOProfileData::getFunctionCounts(StringRef MangledName,
index 2d1193bab0b4e1440c914fc8713259ca3f3e6cdc..6409a10525b4701c6b47ad1b99818449767a0f16 100644 (file)
@@ -33,12 +33,22 @@ private:
   llvm::OwningPtr<llvm::MemoryBuffer> DataBuffer;
   /// Offsets into DataBuffer for each function's counters
   llvm::StringMap<unsigned> DataOffsets;
+  /// Execution counts for each function.
+  llvm::StringMap<uint64_t> FunctionCounts;
+  /// The maximal execution count among all functions.
+  uint64_t MaxFunctionCount;
   CodeGenModule &CGM;
 public:
   PGOProfileData(CodeGenModule &CGM, std::string Path);
   /// Fill Counts with the profile data for the given function name. Returns
   /// false on success.
   bool getFunctionCounts(StringRef MangledName, std::vector<uint64_t> &Counts);
+  /// Return true if a function is hot. If we know nothing about the function,
+  /// return false.
+  bool isHotFunction(StringRef MangledName);
+  /// Return true if a function is cold. If we know nothing about the function,
+  /// return false.
+  bool isColdFunction(StringRef MangledName);
 };
 
 /// Per-function PGO state. This class should generally not be used directly,
diff --git a/test/CodeGen/Inputs/instr-attribute.pgodata b/test/CodeGen/Inputs/instr-attribute.pgodata
new file mode 100644 (file)
index 0000000..0419b3b
--- /dev/null
@@ -0,0 +1,39 @@
+hot_100_percent 4
+100000
+4999950000
+0
+0
+
+hot_40_percent 4
+40000
+799980000
+0
+0
+
+normal_func 4
+20000
+199990000
+0
+0
+
+cold_func 4
+500
+124750
+0
+0
+
+main 13
+1
+100000
+0
+0
+40000
+0
+0
+20000
+0
+0
+500
+0
+0
+
diff --git a/test/CodeGen/instr-attribute.c b/test/CodeGen/instr-attribute.c
new file mode 100644 (file)
index 0000000..917a027
--- /dev/null
@@ -0,0 +1,47 @@
+// Test that instrumentation based profiling sets function attributes correctly.
+
+// RUN: %clang %s -o - -mllvm -disable-llvm-optzns -emit-llvm -S -fprofile-instr-use=%S/Inputs/instr-attribute.pgodata | FileCheck %s
+
+extern int atoi(const char *);
+
+// CHECK: hot_100_percent(i32 %i) [[HOT:#[0-9]+]]
+void hot_100_percent(int i) {
+  while (i > 0)
+    i--;
+}
+
+// CHECK: hot_40_percent(i32 %i) [[HOT]]
+void hot_40_percent(int i) {
+  while (i > 0)
+    i--;
+}
+
+// CHECK: normal_func(i32 %i) [[NORMAL:#[0-9]+]]
+void normal_func(int i) {
+  while (i > 0)
+    i--;
+}
+
+// CHECK: cold_func(i32 %i) [[COLD:#[0-9]+]]
+void cold_func(int i) {
+  while (i > 0)
+    i--;
+}
+
+// CHECK: attributes [[HOT]] = { inlinehint nounwind {{.*}} }
+// CHECK: attributes [[NORMAL]] = { nounwind {{.*}} }
+// CHECK: attributes [[COLD]] = { cold nounwind {{.*}} }
+
+int main(int argc, const char *argv[]) {
+  int max = atoi(argv[1]);
+  int i;
+  for (i = 0; i < max; i++)
+    hot_100_percent(i);
+  for (i = 0; i < max * 4 / 10; i++)
+    hot_40_percent(i);
+  for (i = 0; i < max * 2 / 10; i++)
+    normal_func(i);
+  for (i = 0; i < max / 200; i++)
+    cold_func(i);
+  return 0;
+}