diff options
Diffstat (limited to 'llvm/lib')
| -rw-r--r-- | llvm/lib/IR/DiagnosticInfo.cpp | 11 | ||||
| -rw-r--r-- | llvm/lib/IR/MDBuilder.cpp | 12 | ||||
| -rw-r--r-- | llvm/lib/Transforms/IPO/SampleProfile.cpp | 3 | ||||
| -rw-r--r-- | llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp | 4 | ||||
| -rw-r--r-- | llvm/lib/Transforms/Scalar/LowerExpectIntrinsic.cpp | 31 | ||||
| -rw-r--r-- | llvm/lib/Transforms/Utils/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | llvm/lib/Transforms/Utils/MisExpect.cpp | 177 | 
7 files changed, 231 insertions, 8 deletions
| diff --git a/llvm/lib/IR/DiagnosticInfo.cpp b/llvm/lib/IR/DiagnosticInfo.cpp index 4a8e3cca349..99d5aec3f04 100644 --- a/llvm/lib/IR/DiagnosticInfo.cpp +++ b/llvm/lib/IR/DiagnosticInfo.cpp @@ -370,5 +370,16 @@ std::string DiagnosticInfoOptimizationBase::getMsg() const {    return OS.str();  } +DiagnosticInfoMisExpect::DiagnosticInfoMisExpect(const Instruction *Inst, +                                                 Twine &Msg) +    : DiagnosticInfoWithLocationBase(DK_MisExpect, DS_Warning, +                                     *Inst->getParent()->getParent(), +                                     Inst->getDebugLoc()), +      Msg(Msg) {} + +void DiagnosticInfoMisExpect::print(DiagnosticPrinter &DP) const { +  DP << getLocationStr() << ": " << getMsg(); +} +  void OptimizationRemarkAnalysisFPCommute::anchor() {}  void OptimizationRemarkAnalysisAliasing::anchor() {} diff --git a/llvm/lib/IR/MDBuilder.cpp b/llvm/lib/IR/MDBuilder.cpp index 14bcb3a29b0..7bdb85ace52 100644 --- a/llvm/lib/IR/MDBuilder.cpp +++ b/llvm/lib/IR/MDBuilder.cpp @@ -309,3 +309,15 @@ MDNode *MDBuilder::createIrrLoopHeaderWeight(uint64_t Weight) {    };    return MDNode::get(Context, Vals);  } + +MDNode *MDBuilder::createMisExpect(uint64_t Index, uint64_t LikleyWeight, +                                   uint64_t UnlikleyWeight) { +  auto *IntType = Type::getInt64Ty(Context); +  Metadata *Vals[] = { +      createString("misexpect"), +      createConstant(ConstantInt::get(IntType, Index)), +      createConstant(ConstantInt::get(IntType, LikleyWeight)), +      createConstant(ConstantInt::get(IntType, UnlikleyWeight)), +  }; +  return MDNode::get(Context, Vals); +} diff --git a/llvm/lib/Transforms/IPO/SampleProfile.cpp b/llvm/lib/Transforms/IPO/SampleProfile.cpp index eb4ddfd179b..d0cf63b35f4 100644 --- a/llvm/lib/Transforms/IPO/SampleProfile.cpp +++ b/llvm/lib/Transforms/IPO/SampleProfile.cpp @@ -72,6 +72,7 @@  #include "llvm/Transforms/Instrumentation.h"  #include "llvm/Transforms/Utils/CallPromotionUtils.h"  #include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/MisExpect.h"  #include <algorithm>  #include <cassert>  #include <cstdint> @@ -1446,6 +1447,8 @@ void SampleProfileLoader::propagateWeights(Function &F) {        }      } +    misexpect::verifyMisExpect(TI, Weights, TI->getContext()); +      uint64_t TempWeight;      // Only set weights if there is at least one non-zero weight.      // In any other case, let the analyzer set weights. diff --git a/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp b/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp index 73a91d909a9..c8cf1805c66 100644 --- a/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp +++ b/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp @@ -108,6 +108,7 @@  #include "llvm/Transforms/Instrumentation.h"  #include "llvm/Transforms/Instrumentation/PGOInstrumentation.h"  #include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/MisExpect.h"  #include <algorithm>  #include <cassert>  #include <cstdint> @@ -1776,6 +1777,9 @@ void llvm::setProfMetadata(Module *M, Instruction *TI,                                             : Weights) {      dbgs() << W << " ";    } dbgs() << "\n";); + +  misexpect::verifyMisExpect(TI, Weights, TI->getContext()); +    TI->setMetadata(LLVMContext::MD_prof, MDB.createBranchWeights(Weights));    if (EmitBranchProbability) {      std::string BrCondStr = getBranchCondString(TI); diff --git a/llvm/lib/Transforms/Scalar/LowerExpectIntrinsic.cpp b/llvm/lib/Transforms/Scalar/LowerExpectIntrinsic.cpp index 0d67c0d740e..cdb1d790667 100644 --- a/llvm/lib/Transforms/Scalar/LowerExpectIntrinsic.cpp +++ b/llvm/lib/Transforms/Scalar/LowerExpectIntrinsic.cpp @@ -26,6 +26,7 @@  #include "llvm/Support/CommandLine.h"  #include "llvm/Support/Debug.h"  #include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Utils/MisExpect.h"  using namespace llvm; @@ -71,15 +72,20 @@ static bool handleSwitchExpect(SwitchInst &SI) {    unsigned n = SI.getNumCases(); // +1 for default case.    SmallVector<uint32_t, 16> Weights(n + 1, UnlikelyBranchWeight); -  if (Case == *SI.case_default()) -    Weights[0] = LikelyBranchWeight; -  else -    Weights[Case.getCaseIndex() + 1] = LikelyBranchWeight; +  uint64_t Index = (Case == *SI.case_default()) ? 0 : Case.getCaseIndex() + 1; +  Weights[Index] = LikelyBranchWeight; + +  SI.setMetadata( +      LLVMContext::MD_misexpect, +      MDBuilder(CI->getContext()) +          .createMisExpect(Index, LikelyBranchWeight, UnlikelyBranchWeight)); + +  SI.setCondition(ArgValue); +  misexpect::checkFrontendInstrumentation(SI);    SI.setMetadata(LLVMContext::MD_prof,                   MDBuilder(CI->getContext()).createBranchWeights(Weights)); -  SI.setCondition(ArgValue);    return true;  } @@ -280,19 +286,28 @@ template <class BrSelInst> static bool handleBrSelExpect(BrSelInst &BSI) {    MDBuilder MDB(CI->getContext());    MDNode *Node; +  MDNode *ExpNode;    if ((ExpectedValue->getZExtValue() == ValueComparedTo) == -      (Predicate == CmpInst::ICMP_EQ)) +      (Predicate == CmpInst::ICMP_EQ)) {      Node = MDB.createBranchWeights(LikelyBranchWeight, UnlikelyBranchWeight); -  else +    ExpNode = MDB.createMisExpect(0, LikelyBranchWeight, UnlikelyBranchWeight); +  } else {      Node = MDB.createBranchWeights(UnlikelyBranchWeight, LikelyBranchWeight); +    ExpNode = MDB.createMisExpect(1, LikelyBranchWeight, UnlikelyBranchWeight); +  } -  BSI.setMetadata(LLVMContext::MD_prof, Node); +  BSI.setMetadata(LLVMContext::MD_misexpect, ExpNode);    if (CmpI)      CmpI->setOperand(0, ArgValue);    else      BSI.setCondition(ArgValue); + +  misexpect::checkFrontendInstrumentation(BSI); + +  BSI.setMetadata(LLVMContext::MD_prof, Node); +    return true;  } diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt index c232aa6223c..115f543a8ec 100644 --- a/llvm/lib/Transforms/Utils/CMakeLists.txt +++ b/llvm/lib/Transforms/Utils/CMakeLists.txt @@ -40,6 +40,7 @@ add_llvm_library(LLVMTransformUtils    LowerSwitch.cpp    Mem2Reg.cpp    MetaRenamer.cpp +  MisExpect.cpp    ModuleUtils.cpp    NameAnonGlobals.cpp    PredicateInfo.cpp diff --git a/llvm/lib/Transforms/Utils/MisExpect.cpp b/llvm/lib/Transforms/Utils/MisExpect.cpp new file mode 100644 index 00000000000..26d3402bd27 --- /dev/null +++ b/llvm/lib/Transforms/Utils/MisExpect.cpp @@ -0,0 +1,177 @@ +//===--- MisExpect.cpp - Check the use of llvm.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 the +// llvm.expect intrinsic. This utility extracts the threshold values from +// metadata associated with the instrumented Branch or Switch instruction. The +// threshold values are then used to determine if a warning should be emmited. +// +// MisExpect metadata is generated when llvm.expect intrinsics are lowered see +// LowerExpectIntrinsic.cpp +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Utils/MisExpect.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Analysis/OptimizationRemarkEmitter.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/Support/BranchProbability.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FormatVariadic.h" +#include <cstdint> +#include <functional> +#include <numeric> + +#define DEBUG_TYPE "misexpect" + +using namespace llvm; +using namespace misexpect; + +namespace llvm { + +// Command line option to enable/disable the warning when profile data suggests +// a mismatch with the use of the llvm.expect intrinsic +static cl::opt<bool> PGOWarnMisExpect( +    "pgo-warn-misexpect", cl::init(false), cl::Hidden, +    cl::desc("Use this option to turn on/off " +             "warnings about incorrect usage of llvm.expect intrinsics.")); + +} // namespace llvm + +namespace { + +Instruction *getOprndOrInst(Instruction *I) { +  assert(I != nullptr && "MisExpect target Instruction cannot be nullptr"); +  Instruction *Ret = nullptr; +  if (auto *B = dyn_cast<BranchInst>(I)) { +    Ret = dyn_cast<Instruction>(B->getCondition()); +  } +  // TODO: Find a way to resolve condition location for switches +  // Using the condition of the switch seems to often resolve to an earlier +  // point in the program, i.e. the calculation of the switch condition, rather +  // than the switches location in the source code. Thus, we should use the +  // instruction to get source code locations rather than the condition to +  // improve diagnostic output, such as the caret. If the same problem exists +  // for branch instructions, then we should remove this function and directly +  // use the instruction +  // +  // else if (auto S = dyn_cast<SwitchInst>(I)) { +  // Ret = I; +  //} +  return Ret ? Ret : I; +} + +void emitMisexpectDiagnostic(Instruction *I, LLVMContext &Ctx, +                             uint64_t ProfCount, uint64_t TotalCount) { +  double PercentageCorrect = (double)ProfCount / TotalCount; +  auto PerString = +      formatv("{0:P} ({1} / {2})", PercentageCorrect, ProfCount, TotalCount); +  auto RemStr = formatv( +      "Potential performance regression from use of the llvm.expect intrinsic: " +      "Annotation was correct on {0} of profiled executions.", +      PerString); +  Twine Msg(PerString); +  Instruction *Cond = getOprndOrInst(I); +  if (PGOWarnMisExpect) +    Ctx.diagnose(DiagnosticInfoMisExpect(Cond, Msg)); +  OptimizationRemarkEmitter ORE(I->getParent()->getParent()); +  ORE.emit(OptimizationRemark(DEBUG_TYPE, "misexpect", Cond) << RemStr.str()); +} + +} // namespace + +namespace llvm { +namespace misexpect { + +void verifyMisExpect(Instruction *I, const SmallVector<uint32_t, 4> &Weights, +                     LLVMContext &Ctx) { +  if (auto *MisExpectData = I->getMetadata(LLVMContext::MD_misexpect)) { +    auto *MisExpectDataName = dyn_cast<MDString>(MisExpectData->getOperand(0)); +    if (MisExpectDataName && +        MisExpectDataName->getString().equals("misexpect")) { +      LLVM_DEBUG(llvm::dbgs() << "------------------\n"); +      LLVM_DEBUG(llvm::dbgs() +                 << "Function: " << I->getFunction()->getName() << "\n"); +      LLVM_DEBUG(llvm::dbgs() << "Instruction: " << *I << ":\n"); +      LLVM_DEBUG(for (int Idx = 0, Size = Weights.size(); Idx < Size; ++Idx) { +        llvm::dbgs() << "Weights[" << Idx << "] = " << Weights[Idx] << "\n"; +      }); + +      // extract values from misexpect metadata +      const auto *IndexCint = +          mdconst::dyn_extract<ConstantInt>(MisExpectData->getOperand(1)); +      const auto *LikelyCInt = +          mdconst::dyn_extract<ConstantInt>(MisExpectData->getOperand(2)); +      const auto *UnlikelyCInt = +          mdconst::dyn_extract<ConstantInt>(MisExpectData->getOperand(3)); + +      if (!IndexCint || !LikelyCInt || !UnlikelyCInt) +        return; + +      const uint64_t Index = IndexCint->getZExtValue(); +      const uint64_t LikelyBranchWeight = LikelyCInt->getZExtValue(); +      const uint64_t UnlikelyBranchWeight = UnlikelyCInt->getZExtValue(); +      const uint64_t ProfileCount = Weights[Index]; +      const uint64_t CaseTotal = std::accumulate( +          Weights.begin(), Weights.end(), (uint64_t)0, std::plus<uint64_t>()); +      const uint64_t NumUnlikelyTargets = Weights.size() - 1; + +      const uint64_t TotalBranchWeight = +          LikelyBranchWeight + (UnlikelyBranchWeight * NumUnlikelyTargets); + +      const llvm::BranchProbability LikelyThreshold(LikelyBranchWeight, +                                                    TotalBranchWeight); +      uint64_t ScaledThreshold = LikelyThreshold.scale(CaseTotal); + +      LLVM_DEBUG(llvm::dbgs() +                 << "Unlikely Targets: " << NumUnlikelyTargets << ":\n"); +      LLVM_DEBUG(llvm::dbgs() << "Profile Count: " << ProfileCount << ":\n"); +      LLVM_DEBUG(llvm::dbgs() +                 << "Scaled Threshold: " << ScaledThreshold << ":\n"); +      LLVM_DEBUG(llvm::dbgs() << "------------------\n"); +      if (ProfileCount < ScaledThreshold) +        emitMisexpectDiagnostic(I, Ctx, ProfileCount, CaseTotal); +    } +  } +} + +void checkFrontendInstrumentation(Instruction &I) { +  if (auto *MD = I.getMetadata(LLVMContext::MD_prof)) { +    unsigned NOps = MD->getNumOperands(); + +    // Only emit misexpect diagnostics if at least 2 branch weights are present. +    // Less than 2 branch weights means that the profiling metadata is: +    //    1) incorrect/corrupted +    //    2) not branch weight metadata +    //    3) completely deterministic +    // In these cases we should not emit any diagnostic related to misexpect. +    if (NOps < 3) +      return; + +    // Operand 0 is a string tag "branch_weights" +    if (MDString *Tag = cast<MDString>(MD->getOperand(0))) { +      if (Tag->getString().equals("branch_weights")) { +        SmallVector<uint32_t, 4> RealWeights(NOps - 1); +        for (unsigned i = 1; i < NOps; i++) { +          ConstantInt *Value = +              mdconst::dyn_extract<ConstantInt>(MD->getOperand(i)); +          RealWeights[i - 1] = Value->getZExtValue(); +        } +        verifyMisExpect(&I, RealWeights, I.getContext()); +      } +    } +  } +} + +} // namespace misexpect +} // namespace llvm +#undef DEBUG_TYPE | 

