MisExpect.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. //===--- MisExpect.cpp - Check the use of llvm.expect with PGO data -------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This contains code to emit warnings for potentially incorrect usage of the
  10. // llvm.expect intrinsic. This utility extracts the threshold values from
  11. // metadata associated with the instrumented Branch or Switch instruction. The
  12. // threshold values are then used to determine if a warning should be emmited.
  13. //
  14. // MisExpect's implementation relies on two assumptions about how branch weights
  15. // are managed in LLVM.
  16. //
  17. // 1) Frontend profiling weights are always in place before llvm.expect is
  18. // lowered in LowerExpectIntrinsic.cpp. Frontend based instrumentation therefore
  19. // needs to extract the branch weights and then compare them to the weights
  20. // being added by the llvm.expect intrinsic lowering.
  21. //
  22. // 2) Sampling and IR based profiles will *only* have branch weight metadata
  23. // before profiling data is consulted if they are from a lowered llvm.expect
  24. // intrinsic. These profiles thus always extract the expected weights and then
  25. // compare them to the weights collected during profiling to determine if a
  26. // diagnostic message is warranted.
  27. //
  28. //===----------------------------------------------------------------------===//
  29. #include "llvm/Transforms/Utils/MisExpect.h"
  30. #include "llvm/ADT/Twine.h"
  31. #include "llvm/Analysis/OptimizationRemarkEmitter.h"
  32. #include "llvm/IR/Constants.h"
  33. #include "llvm/IR/DiagnosticInfo.h"
  34. #include "llvm/IR/Instruction.h"
  35. #include "llvm/IR/Instructions.h"
  36. #include "llvm/IR/LLVMContext.h"
  37. #include "llvm/IR/ProfDataUtils.h"
  38. #include "llvm/Support/BranchProbability.h"
  39. #include "llvm/Support/CommandLine.h"
  40. #include "llvm/Support/Debug.h"
  41. #include "llvm/Support/FormatVariadic.h"
  42. #include <algorithm>
  43. #include <cstdint>
  44. #include <functional>
  45. #include <numeric>
  46. #define DEBUG_TYPE "misexpect"
  47. using namespace llvm;
  48. using namespace misexpect;
  49. namespace llvm {
  50. // Command line option to enable/disable the warning when profile data suggests
  51. // a mismatch with the use of the llvm.expect intrinsic
  52. static cl::opt<bool> PGOWarnMisExpect(
  53. "pgo-warn-misexpect", cl::init(false), cl::Hidden,
  54. cl::desc("Use this option to turn on/off "
  55. "warnings about incorrect usage of llvm.expect intrinsics."));
  56. static cl::opt<uint32_t> MisExpectTolerance(
  57. "misexpect-tolerance", cl::init(0),
  58. cl::desc("Prevents emiting diagnostics when profile counts are "
  59. "within N% of the threshold.."));
  60. } // namespace llvm
  61. namespace {
  62. bool isMisExpectDiagEnabled(LLVMContext &Ctx) {
  63. return PGOWarnMisExpect || Ctx.getMisExpectWarningRequested();
  64. }
  65. uint32_t getMisExpectTolerance(LLVMContext &Ctx) {
  66. return std::max(static_cast<uint32_t>(MisExpectTolerance),
  67. Ctx.getDiagnosticsMisExpectTolerance());
  68. }
  69. Instruction *getInstCondition(Instruction *I) {
  70. assert(I != nullptr && "MisExpect target Instruction cannot be nullptr");
  71. Instruction *Ret = nullptr;
  72. if (auto *B = dyn_cast<BranchInst>(I)) {
  73. Ret = dyn_cast<Instruction>(B->getCondition());
  74. }
  75. // TODO: Find a way to resolve condition location for switches
  76. // Using the condition of the switch seems to often resolve to an earlier
  77. // point in the program, i.e. the calculation of the switch condition, rather
  78. // than the switch's location in the source code. Thus, we should use the
  79. // instruction to get source code locations rather than the condition to
  80. // improve diagnostic output, such as the caret. If the same problem exists
  81. // for branch instructions, then we should remove this function and directly
  82. // use the instruction
  83. //
  84. else if (auto *S = dyn_cast<SwitchInst>(I)) {
  85. Ret = dyn_cast<Instruction>(S->getCondition());
  86. }
  87. return Ret ? Ret : I;
  88. }
  89. void emitMisexpectDiagnostic(Instruction *I, LLVMContext &Ctx,
  90. uint64_t ProfCount, uint64_t TotalCount) {
  91. double PercentageCorrect = (double)ProfCount / TotalCount;
  92. auto PerString =
  93. formatv("{0:P} ({1} / {2})", PercentageCorrect, ProfCount, TotalCount);
  94. auto RemStr = formatv(
  95. "Potential performance regression from use of the llvm.expect intrinsic: "
  96. "Annotation was correct on {0} of profiled executions.",
  97. PerString);
  98. Twine Msg(PerString);
  99. Instruction *Cond = getInstCondition(I);
  100. if (isMisExpectDiagEnabled(Ctx))
  101. Ctx.diagnose(DiagnosticInfoMisExpect(Cond, Msg));
  102. OptimizationRemarkEmitter ORE(I->getParent()->getParent());
  103. ORE.emit(OptimizationRemark(DEBUG_TYPE, "misexpect", Cond) << RemStr.str());
  104. }
  105. } // namespace
  106. namespace llvm {
  107. namespace misexpect {
  108. void verifyMisExpect(Instruction &I, ArrayRef<uint32_t> RealWeights,
  109. ArrayRef<uint32_t> ExpectedWeights) {
  110. // To determine if we emit a diagnostic, we need to compare the branch weights
  111. // from the profile to those added by the llvm.expect intrinsic.
  112. // So first, we extract the "likely" and "unlikely" weights from
  113. // ExpectedWeights And determine the correct weight in the profile to compare
  114. // against.
  115. uint64_t LikelyBranchWeight = 0,
  116. UnlikelyBranchWeight = std::numeric_limits<uint32_t>::max();
  117. size_t MaxIndex = 0;
  118. for (size_t Idx = 0, End = ExpectedWeights.size(); Idx < End; Idx++) {
  119. uint32_t V = ExpectedWeights[Idx];
  120. if (LikelyBranchWeight < V) {
  121. LikelyBranchWeight = V;
  122. MaxIndex = Idx;
  123. }
  124. if (UnlikelyBranchWeight > V) {
  125. UnlikelyBranchWeight = V;
  126. }
  127. }
  128. const uint64_t ProfiledWeight = RealWeights[MaxIndex];
  129. const uint64_t RealWeightsTotal =
  130. std::accumulate(RealWeights.begin(), RealWeights.end(), (uint64_t)0,
  131. std::plus<uint64_t>());
  132. const uint64_t NumUnlikelyTargets = RealWeights.size() - 1;
  133. uint64_t TotalBranchWeight =
  134. LikelyBranchWeight + (UnlikelyBranchWeight * NumUnlikelyTargets);
  135. // FIXME: When we've addressed sample profiling, restore the assertion
  136. //
  137. // We cannot calculate branch probability if either of these invariants aren't
  138. // met. However, MisExpect diagnostics should not prevent code from compiling,
  139. // so we simply forgo emitting diagnostics here, and return early.
  140. // assert((TotalBranchWeight >= LikelyBranchWeight) && (TotalBranchWeight > 0)
  141. // && "TotalBranchWeight is less than the Likely branch weight");
  142. if ((TotalBranchWeight == 0) || (TotalBranchWeight <= LikelyBranchWeight))
  143. return;
  144. // To determine our threshold value we need to obtain the branch probability
  145. // for the weights added by llvm.expect and use that proportion to calculate
  146. // our threshold based on the collected profile data.
  147. auto LikelyProbablilty = BranchProbability::getBranchProbability(
  148. LikelyBranchWeight, TotalBranchWeight);
  149. uint64_t ScaledThreshold = LikelyProbablilty.scale(RealWeightsTotal);
  150. // clamp tolerance range to [0, 100)
  151. auto Tolerance = getMisExpectTolerance(I.getContext());
  152. Tolerance = std::clamp(Tolerance, 0u, 99u);
  153. // Allow users to relax checking by N% i.e., if they use a 5% tolerance,
  154. // then we check against 0.95*ScaledThreshold
  155. if (Tolerance > 0)
  156. ScaledThreshold *= (1.0 - Tolerance / 100.0);
  157. // When the profile weight is below the threshold, we emit the diagnostic
  158. if (ProfiledWeight < ScaledThreshold)
  159. emitMisexpectDiagnostic(&I, I.getContext(), ProfiledWeight,
  160. RealWeightsTotal);
  161. }
  162. void checkBackendInstrumentation(Instruction &I,
  163. const ArrayRef<uint32_t> RealWeights) {
  164. SmallVector<uint32_t> ExpectedWeights;
  165. if (!extractBranchWeights(I, ExpectedWeights))
  166. return;
  167. verifyMisExpect(I, RealWeights, ExpectedWeights);
  168. }
  169. void checkFrontendInstrumentation(Instruction &I,
  170. const ArrayRef<uint32_t> ExpectedWeights) {
  171. SmallVector<uint32_t> RealWeights;
  172. if (!extractBranchWeights(I, RealWeights))
  173. return;
  174. verifyMisExpect(I, RealWeights, ExpectedWeights);
  175. }
  176. void checkExpectAnnotations(Instruction &I,
  177. const ArrayRef<uint32_t> ExistingWeights,
  178. bool IsFrontend) {
  179. if (IsFrontend) {
  180. checkFrontendInstrumentation(I, ExistingWeights);
  181. } else {
  182. checkBackendInstrumentation(I, ExistingWeights);
  183. }
  184. }
  185. } // namespace misexpect
  186. } // namespace llvm
  187. #undef DEBUG_TYPE