forked from OSchip/llvm-project
179 lines
7.0 KiB
C++
179 lines
7.0 KiB
C++
//===--- 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/CommandLine.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
|