paulkirth updated this revision to Diff 213167.
paulkirth added a comment.

Update documentation and fix typos


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D65300/new/

https://reviews.llvm.org/D65300

Files:
  clang/include/clang/Basic/CodeGenOptions.def
  clang/include/clang/Driver/Options.td
  clang/lib/CodeGen/CGStmt.cpp
  clang/lib/CodeGen/CMakeLists.txt
  clang/lib/CodeGen/CodeGenFunction.cpp
  clang/lib/CodeGen/CodeGenFunction.h
  clang/lib/CodeGen/MisExpect.cpp
  clang/lib/CodeGen/MisExpect.h
  clang/lib/Driver/ToolChains/Clang.cpp
  clang/lib/Frontend/CompilerInvocation.cpp
  clang/test/Profile/Inputs/misexpect-branch-nonconst-expect-arg.proftext
  clang/test/Profile/Inputs/misexpect-branch.proftext
  clang/test/Profile/Inputs/misexpect-switch-default-only.proftext
  clang/test/Profile/Inputs/misexpect-switch.proftext
  clang/test/Profile/misexpect-branch-cold.c
  clang/test/Profile/misexpect-branch-nonconst-expected-val.c
  clang/test/Profile/misexpect-branch.c
  clang/test/Profile/misexpect-no-warning-without-flag.c
  clang/test/Profile/misexpect-switch-default.c
  clang/test/Profile/misexpect-switch-nonconst.c
  clang/test/Profile/misexpect-switch-only-default-case.c
  clang/test/Profile/misexpect-switch.c

Index: clang/test/Profile/misexpect-switch.c
===================================================================
--- /dev/null
+++ clang/test/Profile/misexpect-switch.c
@@ -0,0 +1,68 @@
+// Test that misexpect detects mis-annotated switch statements
+
+// RUN: llvm-profdata merge %S/Inputs/misexpect-switch.proftext -o %t.profdata
+// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -fmisexpect
+
+int sum(int *buff, int size);
+int random_sample(int *buff, int size);
+int rand();
+
+const int inner_loop = 1000;
+const int outer_loop = 20;
+const int arry_size = 25;
+
+int arry[arry_size] = {0};
+
+void init_arry() {
+  int i;
+  for (i = 0; i < arry_size; ++i) {
+    arry[i] = rand() % 10;
+  }
+}
+
+int main() {
+  init_arry();
+  int val = 0;
+
+  int j, k;
+  for (j = 0; j < outer_loop; ++j) {
+    for (k = 0; k < inner_loop; ++k) {
+      unsigned condition = rand() % 10000;
+      switch (__builtin_expect(condition, 0)) { // expected-warning-re {{Potential performance regression from use of __builtin_expect(): Annotation was correct on {{.+}}% of profiled executions.}}
+      case 0:
+        val += sum(arry, arry_size);
+        break;
+      case 1:
+      case 2:
+      case 3:
+      case 4:
+        val += random_sample(arry, arry_size);
+        break;
+      default:
+        __builtin_unreachable();
+      } // end switch
+    }   // end inner_loop
+  }     // end outer_loop
+
+  return 0;
+}
+
+int sum(int *buff, int size) {
+  int total = 0;
+  int i = 0;
+  for (i = 0; i < size; ++i) {
+    total += buff[i];
+  }
+  return total;
+}
+
+int random_sample(int *buff, int size) {
+  int total = 0;
+  int i;
+  for (i = 0; i < size; ++i) {
+    if (rand() % 5 == 0)
+      total += buff[i];
+  }
+
+  return total;
+}
Index: clang/test/Profile/misexpect-switch-only-default-case.c
===================================================================
--- /dev/null
+++ clang/test/Profile/misexpect-switch-only-default-case.c
@@ -0,0 +1,62 @@
+// Test that misexpect detects mis-annotated switch statements
+
+// RUN: llvm-profdata merge %S/Inputs/misexpect-switch-default-only.proftext -o %t.profdata
+// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -fmisexpect
+
+// expected-no-diagnostics
+int sum(int *buff, int size);
+int random_sample(int *buff, int size);
+int rand();
+
+const int inner_loop = 1000;
+const int outer_loop = 20;
+const int arry_size = 25;
+
+int arry[arry_size] = {0};
+
+void init_arry() {
+  int i;
+  for (i = 0; i < arry_size; ++i) {
+    arry[i] = rand() % 10;
+  }
+}
+
+int main()
+{
+    init_arry();
+    int val = 0;
+
+    int j, k;
+    for (j = 0; j < outer_loop; ++j) {
+        for (k = 0; k < inner_loop; ++k) {
+            unsigned condition = rand() % 10000;
+            switch (__builtin_expect(condition, 0)) {
+            default:
+                val += random_sample(arry, arry_size);
+                break;
+            }; // end switch
+        }      // end inner_loop
+    }          // end outer_loop
+
+    return 0;
+}
+
+int sum(int *buff, int size) {
+  int total = 0;
+  int i = 0;
+  for (i = 0; i < size; ++i) {
+    total += buff[i];
+  }
+  return total;
+}
+
+int random_sample(int *buff, int size) {
+  int total = 0;
+  int i;
+  for (i = 0; i < size; ++i) {
+    if (rand() % 5 == 0)
+      total += buff[i];
+  }
+
+  return total;
+}
Index: clang/test/Profile/misexpect-switch-nonconst.c
===================================================================
--- /dev/null
+++ clang/test/Profile/misexpect-switch-nonconst.c
@@ -0,0 +1,69 @@
+// Test that misexpect detects mis-annotated switch statements
+
+// RUN: llvm-profdata merge %S/Inputs/misexpect-switch.proftext -o %t.profdata
+// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -fmisexpect
+
+// expected-no-diagnostics
+int sum(int *buff, int size);
+int random_sample(int *buff, int size);
+int rand();
+
+const int inner_loop = 1000;
+const int outer_loop = 20;
+const int arry_size = 25;
+
+int arry[arry_size] = {0};
+
+void init_arry() {
+  int i;
+  for (i = 0; i < arry_size; ++i) {
+    arry[i] = rand() % 10;
+  }
+}
+
+int main() {
+  init_arry();
+  int val = 0;
+
+  int j, k;
+  for (j = 0; j < outer_loop; ++j) {
+    for (k = 0; k < inner_loop; ++k) {
+      unsigned condition = rand() % 10000;
+      switch (__builtin_expect(condition, rand())) {
+      case 0:
+        val += sum(arry, arry_size);
+        break;
+      case 1:
+      case 2:
+      case 3:
+      case 4:
+        val += random_sample(arry, arry_size);
+        break;
+      default:
+        __builtin_unreachable();
+      } // end switch
+    }   // end inner_loop
+  }     // end outer_loop
+
+  return 0;
+}
+
+int sum(int *buff, int size) {
+  int total = 0;
+  int i = 0;
+  for (i = 0; i < size; ++i) {
+    total += buff[i];
+  }
+  return total;
+}
+
+int random_sample(int *buff, int size) {
+  int total = 0;
+  int i;
+  for (i = 0; i < size; ++i) {
+    if (rand() % 5 == 0)
+      total += buff[i];
+  }
+
+  return total;
+}
Index: clang/test/Profile/misexpect-switch-default.c
===================================================================
--- /dev/null
+++ clang/test/Profile/misexpect-switch-default.c
@@ -0,0 +1,68 @@
+// Test that misexpect detects mis-annotated switch statements
+
+// RUN: llvm-profdata merge %S/Inputs/misexpect-switch.proftext -o %t.profdata
+// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -fmisexpect
+
+int sum(int *buff, int size);
+int random_sample(int *buff, int size);
+int rand();
+
+const int inner_loop = 1000;
+const int outer_loop = 20;
+const int arry_size = 25;
+
+int arry[arry_size] = {0};
+
+void init_arry() {
+  int i;
+  for (i = 0; i < arry_size; ++i) {
+    arry[i] = rand() % 10;
+  }
+}
+
+int main() {
+  init_arry();
+  int val = 0;
+
+  int j, k;
+  for (j = 0; j < outer_loop; ++j) {
+    for (k = 0; k < inner_loop; ++k) {
+      unsigned condition = rand() % 5;
+      switch (__builtin_expect(condition, 6)) { // expected-warning-re {{Potential performance regression from use of __builtin_expect(): Annotation was correct on {{.+}}% of profiled executions.}}
+      case 0:
+        val += sum(arry, arry_size);
+        break;
+      case 1:
+      case 2:
+      case 3:
+      case 4:
+        val += random_sample(arry, arry_size);
+        break;
+      default:
+        __builtin_unreachable();
+      } // end switch
+    }   // end inner_loop
+  }     // end outer_loop
+
+  return 0;
+}
+
+int sum(int *buff, int size) {
+  int total = 0;
+  int i = 0;
+  for (i = 0; i < size; ++i) {
+    total += buff[i];
+  }
+  return total;
+}
+
+int random_sample(int *buff, int size) {
+  int total = 0;
+  int i;
+  for (i = 0; i < size; ++i) {
+    if (rand() % 5 == 0)
+      total += buff[i];
+  }
+
+  return total;
+}
Index: clang/test/Profile/misexpect-no-warning-without-flag.c
===================================================================
--- /dev/null
+++ clang/test/Profile/misexpect-no-warning-without-flag.c
@@ -0,0 +1,51 @@
+// Test that misexpect detects mis-annotated branches
+
+// RUN: llvm-profdata merge %S/Inputs/misexpect-branch.proftext -o %t.profdata
+// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify
+
+// expected-no-diagnostics
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+int foo(int);
+int bar();
+int baz(int);
+int buzz();
+
+const int inner_loop = 100;
+const int outer_loop = 2000;
+
+int main() {
+  int val = 0;
+  for (int i = 0; i < outer_loop; ++i) {
+    for (int i = 0; i < inner_loop; ++i) {
+      val = bar();
+    }
+  }
+  return 0;
+}
+
+int bar() {
+  int rando = buzz();
+  int x = 0;
+  if (likely(rando % (outer_loop * inner_loop) == 0)) {
+    x = baz(rando);
+  } else {
+    x = foo(50);
+  }
+  return x;
+}
+
+int foo(int i) {
+  int j = 0;
+  while (i < 100) {
+    i += buzz() % 5;
+    j++;
+  }
+  return j;
+}
+
+int baz(int rando) {
+  int x = rando;
+  return x;
+}
Index: clang/test/Profile/misexpect-branch.c
===================================================================
--- /dev/null
+++ clang/test/Profile/misexpect-branch.c
@@ -0,0 +1,50 @@
+// Test that misexpect detects mis-annotated branches
+
+// RUN: llvm-profdata merge %S/Inputs/misexpect-branch.proftext -o %t.profdata
+// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -fmisexpect
+
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+int foo(int);
+int bar();
+int baz(int);
+int buzz();
+
+const int inner_loop = 100;
+const int outer_loop = 2000;
+
+int main() {
+  int val = 0;
+  for (int i = 0; i < outer_loop; ++i) {
+    for (int i = 0; i < inner_loop; ++i) {
+      val = bar();
+    }
+  }
+  return 0;
+}
+
+int bar() {
+  int rando = buzz();
+  int x = 0;
+  if (likely(rando % (outer_loop * inner_loop) == 0)) { // expected-warning-re {{Potential performance regression from use of __builtin_expect(): Annotation was correct on {{.+}}% of profiled executions.}}
+    x = baz(rando);
+  } else {
+    x = foo(50);
+  }
+  return x;
+}
+
+int foo(int i) {
+  int j = 0;
+  while (i < 100) {
+    i += buzz() % 5;
+    j++;
+  }
+  return j;
+}
+
+int baz(int rando) {
+  int x = rando;
+  return x;
+}
Index: clang/test/Profile/misexpect-branch-nonconst-expected-val.c
===================================================================
--- /dev/null
+++ clang/test/Profile/misexpect-branch-nonconst-expected-val.c
@@ -0,0 +1,51 @@
+// Test that misexpect detects mis-annotated branches
+
+// RUN: llvm-profdata merge %S/Inputs/misexpect-branch-nonconst-expect-arg.proftext -o %t.profdata
+// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -fmisexpect
+
+// expected-no-diagnostics
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+int foo(int);
+int bar();
+int baz(int);
+int buzz();
+
+const int inner_loop = 100;
+const int outer_loop = 2000;
+
+int main() {
+  int val = 0;
+  for (int i = 0; i < outer_loop; ++i) {
+    for (int i = 0; i < inner_loop; ++i) {
+      val = bar();
+    }
+  }
+  return 0;
+}
+
+int bar() {
+  int rando = buzz();
+  int x = 0;
+  if (__builtin_expect(rando % (outer_loop * inner_loop) == 0, buzz())) {
+    x = baz(rando);
+  } else {
+    x = foo(50);
+  }
+  return x;
+}
+
+int foo(int i) {
+  int j = 0;
+  while (i < 100) {
+    i += buzz() % 5;
+    j++;
+  }
+  return j;
+}
+
+int baz(int rando) {
+  int x = rando;
+  return x;
+}
Index: clang/test/Profile/misexpect-branch-cold.c
===================================================================
--- /dev/null
+++ clang/test/Profile/misexpect-branch-cold.c
@@ -0,0 +1,51 @@
+// Test that misexpect detects mis-annotated branches
+
+// RUN: llvm-profdata merge %S/Inputs/misexpect-branch.proftext -o %t.profdata
+// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -fmisexpect
+
+// expected-no-diagnostics
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+int foo(int);
+int bar();
+int baz(int);
+int buzz();
+
+const int inner_loop = 100;
+const int outer_loop = 2000;
+
+int main() {
+  int val = 0;
+  for (int i = 0; i < outer_loop; ++i) {
+    for (int i = 0; i < inner_loop; ++i) {
+      val = bar();
+    }
+  }
+  return 0;
+}
+
+int bar() {
+  int rando = buzz();
+  int x = 0;
+  if (unlikely(rando % (outer_loop * inner_loop) == 0)) {
+    x = baz(rando);
+  } else {
+    x = foo(50);
+  }
+  return x;
+}
+
+int foo(int i) {
+  int j = 0;
+  while (i < 100) {
+    i += buzz() % 5;
+    j++;
+  }
+  return j;
+}
+
+int baz(int rando) {
+  int x = rando;
+  return x;
+}
Index: clang/test/Profile/Inputs/misexpect-switch.proftext
===================================================================
--- /dev/null
+++ clang/test/Profile/Inputs/misexpect-switch.proftext
@@ -0,0 +1,45 @@
+init_arry
+# Func Hash:
+18129
+# Num Counters:
+2
+# Counter Values:
+1
+25
+
+main
+# Func Hash:
+1965403898329309329
+# Num Counters:
+10
+# Counter Values:
+1
+20
+20000
+20000
+5
+19995
+0
+0
+0
+0
+
+random_sample
+# Func Hash:
+19458874217560
+# Num Counters:
+3
+# Counter Values:
+19995
+499875
+100042
+
+sum
+# Func Hash:
+1160280
+# Num Counters:
+2
+# Counter Values:
+5
+125
+
Index: clang/test/Profile/Inputs/misexpect-switch-default-only.proftext
===================================================================
--- /dev/null
+++ clang/test/Profile/Inputs/misexpect-switch-default-only.proftext
@@ -0,0 +1,40 @@
+init_arry
+# Func Hash:
+18129
+# Num Counters:
+2
+# Counter Values:
+1
+25
+
+main
+# Func Hash:
+79676873694057560
+# Num Counters:
+5
+# Counter Values:
+1
+20
+20000
+20000
+20000
+
+random_sample
+# Func Hash:
+19458874217560
+# Num Counters:
+3
+# Counter Values:
+20000
+500000
+100006
+
+sum
+# Func Hash:
+1160280
+# Num Counters:
+2
+# Counter Values:
+0
+0
+
Index: clang/test/Profile/Inputs/misexpect-branch.proftext
===================================================================
--- /dev/null
+++ clang/test/Profile/Inputs/misexpect-branch.proftext
@@ -0,0 +1,36 @@
+bar
+# Func Hash:
+45795613684824
+# Num Counters:
+2
+# Counter Values:
+200000
+0
+
+baz
+# Func Hash:
+24
+# Num Counters:
+1
+# Counter Values:
+0
+
+foo
+# Func Hash:
+635992
+# Num Counters:
+2
+# Counter Values:
+200000
+5095099
+
+main
+# Func Hash:
+303943193688
+# Num Counters:
+3
+# Counter Values:
+1
+2000
+200000
+
Index: clang/test/Profile/Inputs/misexpect-branch-nonconst-expect-arg.proftext
===================================================================
--- /dev/null
+++ clang/test/Profile/Inputs/misexpect-branch-nonconst-expect-arg.proftext
@@ -0,0 +1,36 @@
+bar
+# Func Hash:
+11262309464
+# Num Counters:
+2
+# Counter Values:
+200000
+2
+
+baz
+# Func Hash:
+24
+# Num Counters:
+1
+# Counter Values:
+2
+
+foo
+# Func Hash:
+635992
+# Num Counters:
+2
+# Counter Values:
+199998
+5096876
+
+main
+# Func Hash:
+303943193688
+# Num Counters:
+3
+# Counter Values:
+1
+2000
+200000
+
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -809,6 +809,7 @@
       << Args.getLastArg(OPT_fprofile_remapping_file_EQ)->getAsString(Args)
       << "-fexperimental-new-pass-manager";
   }
+  Opts.MisExpect = Args.hasFlag(OPT_fmisexpect, OPT_fno_misexpect, false);
 
   Opts.CoverageMapping =
       Args.hasFlag(OPT_fcoverage_mapping, OPT_fno_coverage_mapping, false);
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -3998,6 +3998,11 @@
   Args.AddLastArg(CmdArgs, options::OPT_ffine_grained_bitfield_accesses,
                   options::OPT_fno_fine_grained_bitfield_accesses);
 
+  if (Args.hasFlag(options::OPT_fmisexpect, options::OPT_fno_misexpect,
+                   false)) {
+    CmdArgs.push_back("-fmisexpect");
+  }
+
   // Handle segmented stacks.
   if (Args.hasArg(options::OPT_fsplit_stack))
     CmdArgs.push_back("-split-stacks");
Index: clang/lib/CodeGen/MisExpect.h
===================================================================
--- /dev/null
+++ clang/lib/CodeGen/MisExpect.h
@@ -0,0 +1,62 @@
+//===--- MisExpect.h - Check Use of __builtin_expect() with PGO data ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This contains code to emit warnings for potentially incorrect usage of
+// __builtin_expect(). It uses PGO profiles to validation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_CODEGEN_MISEXPECT_H
+#define LLVM_CLANG_LIB_CODEGEN_MISEXPECT_H
+
+#include "CodeGenModule.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/Optional.h"
+
+namespace clang {
+namespace CodeGen {
+namespace MisExpect {
+
+/// getExpectedValue - Returns the value that __builtin_expect() is expecting.
+/// If the second parameter cannot be evaluated at compile-time, returns an
+/// empty Optional. Returns None when Call is not __builtin_expect()
+/// \param Call the call expression to __builtin_expect()
+/// \param Context the current ASTContext
+Optional<int64_t> getExpectedValue(const clang::CallExpr *Call,
+                                   ASTContext &Context);
+
+/// CheckMisExpectBranch - check if a branch is annotated with
+/// __builtin_expect and when using profiling data, verify that the profile
+/// agrees with the use of the annotation
+/// \param Cond the conditional expression being checked
+/// \param TrueCount the profile counter for this block
+/// \param CurrProfCount the current total profile count
+/// \param CGM a reference to the current CodeGenModule
+void CheckMisExpectBranch(const Expr *Cond, uint64_t TrueCount,
+                          uint64_t CurrProfCount, CodeGenModule &CGM);
+
+/// CheckMisExpect - check if a branch is annotated with __builtin_expect and
+/// when using profiling data, verify that the profile agrees with the use of
+/// the annotation
+/// \param Call the call expression to __builtin_expect()
+/// \param SwitchWeights pointer to a vector of profile counts for each case arm
+/// \param CaseMap a table mapping the constant value of a case target to its
+/// index in the SwitchWeights vector
+/// \param CGM a reference to the current CodeGenModule
+void CheckMisExpectSwitch(const CallExpr *Call,
+                          llvm::SmallVector<uint64_t, 16> *SwitchWeights,
+                          llvm::DenseMap<int64_t, size_t> *CaseMap,
+                          CodeGenModule &CGM);
+
+} // namespace MisExpect
+} // namespace CodeGen
+} // namespace clang
+
+#endif // LLVM_CLANG_LIB_CODEGEN_MISEXPECT_H
Index: clang/lib/CodeGen/MisExpect.cpp
===================================================================
--- /dev/null
+++ clang/lib/CodeGen/MisExpect.cpp
@@ -0,0 +1,187 @@
+//===--- MisExpect.cpp - Check Use of __builtin_expect() with PGO data ----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This contains code to emit warnings for potentially incorrect usage of
+// __builtin_expect(). It uses PGO profiles for validation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisExpect.h"
+#include "CodeGenModule.h"
+#include "clang/Basic/Builtins.h"
+#include "clang/Basic/CodeGenOptions.h"
+#include "clang/Basic/Diagnostic.h"
+#include "llvm/Support/BranchProbability.h"
+
+#include <algorithm>
+#include <numeric>
+
+namespace {
+
+using namespace clang;
+using namespace clang::CodeGen;
+
+// Emit Warning notifying user that the current PGO counter is a mismatch with
+// the use of __builtin_expect()
+// \param PercentageCorrect the percentage the expected target of
+// __builtin_expect() was taken during profiling as an integer
+void EmitMisExpectWarning(const clang::CallExpr *Call, CodeGenModule &CGM,
+                          unsigned PercentageCorrect) {
+  SourceLocation ExprLoc = Call->getBeginLoc();
+  unsigned DiagID = CGM.getDiags().getCustomDiagID(
+      DiagnosticsEngine::Warning,
+      "Potential performance regression from use of __builtin_expect(): "
+      "Annotation was correct on %0%% of profiled executions.");
+
+  CGM.getDiags()
+      .Report(ExprLoc, DiagID)
+      .AddTaggedVal(PercentageCorrect,
+                    clang::DiagnosticsEngine::ArgumentKind::ak_uint);
+}
+
+// Prints some debug diagnostics useful when checking SwitchStmts.
+// Allows for simple comparison of the Case Value mappings to their index in the
+// SwitchWeights data structure in CGStmts.cpp
+void DebugPrintMisExpectSwitchInfo(SmallVector<uint64_t, 16> *SwitchWeights,
+                                   llvm::DenseMap<int64_t, size_t> *CaseMap) {
+  auto size = SwitchWeights->size();
+  for (size_t i = 0; i < size; ++i) {
+    llvm::dbgs() << "Index: " << i << "\tProfile Value:\t"
+                 << (*SwitchWeights)[i] << "\n";
+  }
+
+  llvm::dbgs() << "------------------\n";
+
+  for (auto &item : *CaseMap) {
+    llvm::dbgs() << "Case Key: " << item.first << "\tRelated Index:\t"
+                 << item.second << "\n";
+  }
+
+  llvm::dbgs() << "------------------\n";
+  uint64_t CaseTotal =
+      std::accumulate(SwitchWeights->begin(), SwitchWeights->end(), 0);
+  llvm::dbgs() << "Switch Profile Count:\t" << CaseTotal << "\n";
+}
+
+} // namespace
+
+namespace clang {
+namespace CodeGen {
+namespace MisExpect {
+
+#define DEBUG_TYPE "misexpect"
+
+Optional<int64_t> getExpectedValue(const clang::CallExpr *Call,
+                                   ASTContext &Context) {
+  if (!Call)
+    return Optional<int64_t>(llvm::None);
+
+  auto *FD = dyn_cast_or_null<FunctionDecl>(Call->getCalleeDecl());
+  if (!FD || FD->getBuiltinID() != Builtin::BI__builtin_expect) {
+    return Optional<int64_t>(llvm::None);
+  }
+
+  // Check if we can evaluate the 2nd parameter to __builtin_expect(expr,long)
+  // since it may not be able to be evaluated at compile-time
+  Expr::EvalResult ExprResult;
+  auto Arg = Call->getArg(1);
+  Arg->EvaluateAsInt(ExprResult, Context, Expr::SE_AllowSideEffects);
+
+  if (!ExprResult.Val.hasValue())
+    return Optional<int64_t>(llvm::None);
+
+  llvm::APSInt &Into = ExprResult.Val.getInt();
+  int64_t ExpectedVal = Into.getExtValue();
+  return ExpectedVal;
+}
+
+void CheckMisExpectBranch(const Expr *Cond, uint64_t TrueCount,
+                          uint64_t CurrProfCount, CodeGenModule &CGM) {
+  if (!CGM.getCodeGenOpts().MisExpect ||
+      CGM.getCodeGenOpts().getProfileUse() == CodeGenOptions::ProfileNone)
+    return;
+
+  auto *Call = dyn_cast<CallExpr>(Cond->IgnoreImpCasts());
+
+  Optional<int64_t> ExpectedValOpt = getExpectedValue(Call, CGM.getContext());
+
+  if (!ExpectedValOpt.hasValue())
+    return;
+
+  const long ExpectedVal = ExpectedValOpt.getValue();
+  const bool ExpectedTrueBranch = (ExpectedVal != 0);
+
+  LLVM_DEBUG(llvm::dbgs() << "Expected Value:\t" << ExpectedVal << "\n");
+  LLVM_DEBUG(llvm::dbgs() << "Current Count:\t" << CurrProfCount << "\n");
+  LLVM_DEBUG(llvm::dbgs() << "True Count:\t" << TrueCount << "\n");
+
+  bool IncorrectPerfCounters = false;
+  uint64_t Scaled;
+  unsigned Percentage;
+
+  // TODO: determine better heuristics than hot/cold function thresholds
+  // Currently {Hot|Cold}FunctionThreshold is taken from
+  // PGOInstrumentation.cpp:1084
+  if (ExpectedTrueBranch) {
+    const llvm::BranchProbability HotFunctionThreshold(1, 100);
+    Scaled = HotFunctionThreshold.scale(CurrProfCount);
+    Percentage = (TrueCount / (float)CurrProfCount) * 100;
+    if (TrueCount < Scaled)
+      IncorrectPerfCounters = true;
+  } else {
+    const llvm::BranchProbability ColdFunctionThreshold(2, 10000);
+    Scaled = ColdFunctionThreshold.scale(CurrProfCount);
+    Percentage = ((CurrProfCount - TrueCount) / (float)CurrProfCount) * 100;
+    if (TrueCount > Scaled)
+      IncorrectPerfCounters = true;
+  }
+  LLVM_DEBUG(llvm::dbgs() << "Scaled Count:\t" << Scaled << "\n");
+
+  if (IncorrectPerfCounters)
+    EmitMisExpectWarning(Call, CGM, Percentage);
+}
+
+void CheckMisExpectSwitch(const CallExpr *Call,
+                          SmallVector<uint64_t, 16> *SwitchWeights,
+                          llvm::DenseMap<int64_t, size_t> *CaseMap,
+                          CodeGenModule &CGM) {
+  if (!SwitchWeights || !CaseMap)
+    return;
+
+  Optional<int64_t> ExpectedValOpt = getExpectedValue(Call, CGM.getContext());
+
+  if (!ExpectedValOpt.hasValue())
+    return;
+
+  LLVM_DEBUG(DebugPrintMisExpectSwitchInfo(SwitchWeights, CaseMap));
+  const long ExpectedVal = ExpectedValOpt.getValue();
+
+  LLVM_DEBUG(llvm::dbgs() << "Expected Value:\t" << ExpectedVal << "\n");
+
+  uint64_t Max =
+      *std::max_element(SwitchWeights->begin(), SwitchWeights->end());
+
+  auto MapIndex = CaseMap->find(ExpectedVal);
+
+  uint64_t Index = (MapIndex != CaseMap->end()) ? MapIndex->second : 0;
+  uint64_t TakenCount = (*SwitchWeights)[Index];
+
+  LLVM_DEBUG(llvm::dbgs() << "Taken Count:\t" << TakenCount << "\n");
+  LLVM_DEBUG(llvm::dbgs() << "Max Count:\t" << Max << "\n");
+  uint64_t CaseTotal =
+      std::accumulate(SwitchWeights->begin(), SwitchWeights->end(), 0);
+  unsigned Percentage = ((float)TakenCount / (float)CaseTotal) * 100;
+
+  if (TakenCount < Max)
+    EmitMisExpectWarning(Call, CGM, Percentage);
+}
+#undef DEBUG_TYPE
+
+} // namespace MisExpect
+} // namespace CodeGen
+} // namespace clang
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -1370,6 +1370,8 @@
   llvm::SwitchInst *SwitchInsn = nullptr;
   /// The branch weights of SwitchInsn when doing instrumentation based PGO.
   SmallVector<uint64_t, 16> *SwitchWeights = nullptr;
+  llvm::DenseMap<int64_t, size_t> *CaseMap = nullptr;
+
 
   /// CaseRangeBlock - This block holds if condition check for last case
   /// statement range in current switch instruction.
Index: clang/lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.cpp
+++ clang/lib/CodeGen/CodeGenFunction.cpp
@@ -20,6 +20,7 @@
 #include "CodeGenModule.h"
 #include "CodeGenPGO.h"
 #include "TargetInfo.h"
+#include "MisExpect.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTLambda.h"
 #include "clang/AST/Decl.h"
@@ -1346,8 +1347,6 @@
   return true;
 }
 
-
-
 /// EmitBranchOnBoolExpr - Emit a branch on a boolean condition (e.g. for an if
 /// statement) to the specified blocks.  Based on the condition, this might try
 /// to simplify the codegen of the conditional based on the branch.
@@ -1358,6 +1357,8 @@
                                            uint64_t TrueCount) {
   Cond = Cond->IgnoreParens();
 
+  MisExpect::CheckMisExpectBranch(Cond, TrueCount, getCurrentProfileCount(), CGM);
+
   if (const BinaryOperator *CondBOp = dyn_cast<BinaryOperator>(Cond)) {
 
     // Handle X && Y in a condition.
Index: clang/lib/CodeGen/CMakeLists.txt
===================================================================
--- clang/lib/CodeGen/CMakeLists.txt
+++ clang/lib/CodeGen/CMakeLists.txt
@@ -87,6 +87,7 @@
   ItaniumCXXABI.cpp
   MacroPPCallbacks.cpp
   MicrosoftCXXABI.cpp
+  MisExpect.cpp
   ModuleBuilder.cpp
   ObjectFilePCHContainerOperations.cpp
   PatternInit.cpp
Index: clang/lib/CodeGen/CGStmt.cpp
===================================================================
--- clang/lib/CodeGen/CGStmt.cpp
+++ clang/lib/CodeGen/CGStmt.cpp
@@ -14,6 +14,7 @@
 #include "CGDebugInfo.h"
 #include "CodeGenModule.h"
 #include "TargetInfo.h"
+#include "MisExpect.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/PrettyStackTrace.h"
@@ -1284,8 +1285,16 @@
 
   llvm::BasicBlock *CaseDest = createBasicBlock("sw.bb");
   EmitBlockWithFallThrough(CaseDest, &S);
-  if (SwitchWeights)
+  size_t MapIndex = 0;
+  if (SwitchWeights) {
     SwitchWeights->push_back(getProfileCount(&S));
+    if (CaseMap) {
+      MapIndex = SwitchWeights->size() - 1;
+      llvm::ConstantInt *CaseVal =
+          Builder.getInt(S.getLHS()->EvaluateKnownConstInt(getContext()));
+      (*CaseMap)[CaseVal->getSExtValue()] = MapIndex;
+    }
+  }
   SwitchInsn->addCase(CaseVal, CaseDest);
 
   // Recursively emitting the statement is acceptable, but is not wonderful for
@@ -1306,8 +1315,15 @@
     llvm::ConstantInt *CaseVal =
       Builder.getInt(CurCase->getLHS()->EvaluateKnownConstInt(getContext()));
 
-    if (SwitchWeights)
+    if (SwitchWeights) {
       SwitchWeights->push_back(getProfileCount(NextCase));
+      if (CaseMap) {
+        // only the original statement gets a counter, so map back to it's index
+        // in SwitchWeights
+        (*CaseMap)[CaseVal->getSExtValue()] = MapIndex;
+      }
+    }
+
     if (CGM.getCodeGenOpts().hasProfileClangInstr()) {
       CaseDest = createBasicBlock("sw.bb");
       EmitBlockWithFallThrough(CaseDest, &S);
@@ -1575,6 +1591,7 @@
   // Handle nested switch statements.
   llvm::SwitchInst *SavedSwitchInsn = SwitchInsn;
   SmallVector<uint64_t, 16> *SavedSwitchWeights = SwitchWeights;
+  llvm::DenseMap<int64_t, size_t> *SavedCaseMap = CaseMap;
   llvm::BasicBlock *SavedCRBlock = CaseRangeBlock;
 
   // See if we can constant fold the condition of the switch and therefore only
@@ -1646,6 +1663,8 @@
     }
     SwitchWeights = new SmallVector<uint64_t, 16>();
     SwitchWeights->reserve(NumCases);
+    CaseMap = new llvm::DenseMap<int64_t, size_t>();
+    CaseMap->reserve(NumCases);
     // The default needs to be first. We store the edge count, so we already
     // know the right weight.
     SwitchWeights->push_back(DefaultCount);
@@ -1698,10 +1717,15 @@
   auto *Call = dyn_cast<CallExpr>(S.getCond());
   if (Call && CGM.getCodeGenOpts().OptimizationLevel != 0) {
     auto *FD = dyn_cast_or_null<FunctionDecl>(Call->getCalleeDecl());
-    if (FD && FD->getBuiltinID() == Builtin::BI__builtin_unpredictable) {
-      llvm::MDBuilder MDHelper(getLLVMContext());
-      SwitchInsn->setMetadata(llvm::LLVMContext::MD_unpredictable,
-                              MDHelper.createUnpredictable());
+    if (FD) {
+      if (FD->getBuiltinID() == Builtin::BI__builtin_unpredictable) {
+        llvm::MDBuilder MDHelper(getLLVMContext());
+        SwitchInsn->setMetadata(llvm::LLVMContext::MD_unpredictable,
+                                MDHelper.createUnpredictable());
+      } else if (CGM.getCodeGenOpts().MisExpect &&
+                 FD->getBuiltinID() == Builtin::BI__builtin_expect) {
+        MisExpect::CheckMisExpectSwitch(Call, SwitchWeights, CaseMap, CGM);
+      }
     }
   }
 
@@ -1714,8 +1738,14 @@
                               createProfileWeights(*SwitchWeights));
     delete SwitchWeights;
   }
+
+  if (CaseMap) {
+    delete CaseMap;
+  }
+
   SwitchInsn = SavedSwitchInsn;
   SwitchWeights = SavedSwitchWeights;
+  CaseMap = SavedCaseMap;
   CaseRangeBlock = SavedCRBlock;
 }
 
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -716,6 +716,11 @@
     Group<f_Group>, Alias<fprofile_sample_accurate>;
 def fno_auto_profile_accurate : Flag<["-"], "fno-auto-profile-accurate">,
     Group<f_Group>, Alias<fno_profile_sample_accurate>;
+def fmisexpect : Flag<["-"], "fmisexpect">,
+    Group<f_Group>, Flags<[CC1Option]>,
+    HelpText<"Validate use of __builtin_expect with instrumentation data">;
+def fno_misexpect : Flag<["-"], "fno-misexpect">,
+    Group<f_Group>, Flags<[CC1Option]>;
 def fdebug_compilation_dir : Separate<["-"], "fdebug-compilation-dir">,
     Group<f_Group>, Flags<[CC1Option, CC1AsOption, CoreOption]>,
     HelpText<"The compilation directory to embed in the debug info.">;
Index: clang/include/clang/Basic/CodeGenOptions.def
===================================================================
--- clang/include/clang/Basic/CodeGenOptions.def
+++ clang/include/clang/Basic/CodeGenOptions.def
@@ -169,6 +169,7 @@
                                    ///< enable code coverage analysis.
 CODEGENOPT(DumpCoverageMapping , 1, 0) ///< Dump the generated coverage mapping
                                        ///< regions.
+CODEGENOPT(MisExpect , 1, 0) ///< Validate __builtin_expect with PGO counters
 
   /// If -fpcc-struct-return or -freg-struct-return is specified.
 ENUM_CODEGENOPT(StructReturnConvention, StructReturnConventionKind, 2, SRCK_Default)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to