There is a case when branch profile metadata is OK to miss, namely, cold functions. The goal of the RFC (see the referenced issue) is to avoid accidental omission (and, at a later date, corruption) of profile metadata. However, asking cold functions to have all their conditional branches marked with "0" probabilities would be overdoing it. We can just ask cold functions to have an explicit 0 entry count. This patch: - injects an entry count for functions, unless they have one (synthetic or not) - if the entry count is 0, doesn't inject, nor does it verify the rest of the metadata - at verification, if the entry count is missing, it reports an error Issue #147390
156 lines
6.1 KiB
C++
156 lines
6.1 KiB
C++
//===- ProfileVerify.cpp - Verify profile info for testing ----------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Transforms/Utils/ProfileVerify.h"
|
|
#include "llvm/ADT/DynamicAPInt.h"
|
|
#include "llvm/ADT/PostOrderIterator.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/Analysis/BranchProbabilityInfo.h"
|
|
#include "llvm/Analysis/LoopInfo.h"
|
|
#include "llvm/IR/Analysis.h"
|
|
#include "llvm/IR/Dominators.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/IR/MDBuilder.h"
|
|
#include "llvm/IR/ProfDataUtils.h"
|
|
#include "llvm/Support/BranchProbability.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
|
|
using namespace llvm;
|
|
static cl::opt<int64_t>
|
|
DefaultFunctionEntryCount("profcheck-default-function-entry-count",
|
|
cl::init(1000));
|
|
namespace {
|
|
class ProfileInjector {
|
|
Function &F;
|
|
FunctionAnalysisManager &FAM;
|
|
|
|
public:
|
|
static const Instruction *
|
|
getTerminatorBenefitingFromMDProf(const BasicBlock &BB) {
|
|
if (succ_size(&BB) < 2)
|
|
return nullptr;
|
|
auto *Term = BB.getTerminator();
|
|
return (isa<BranchInst>(Term) || isa<SwitchInst>(Term) ||
|
|
isa<IndirectBrInst>(Term) || isa<CallBrInst>(Term))
|
|
? Term
|
|
: nullptr;
|
|
}
|
|
|
|
static Instruction *getTerminatorBenefitingFromMDProf(BasicBlock &BB) {
|
|
return const_cast<Instruction *>(
|
|
getTerminatorBenefitingFromMDProf(const_cast<const BasicBlock &>(BB)));
|
|
}
|
|
|
|
ProfileInjector(Function &F, FunctionAnalysisManager &FAM) : F(F), FAM(FAM) {}
|
|
bool inject();
|
|
};
|
|
} // namespace
|
|
|
|
// FIXME: currently this injects only for terminators. Select isn't yet
|
|
// supported.
|
|
bool ProfileInjector::inject() {
|
|
// Get whatever branch probability info can be derived from the given IR -
|
|
// whether it has or not metadata. The main intention for this pass is to
|
|
// ensure that other passes don't drop or "forget" to update MD_prof. We do
|
|
// this as a mode in which lit tests would run. We want to avoid changing the
|
|
// behavior of those tests. A pass may use BPI (or BFI, which is computed from
|
|
// BPI). If no metadata is present, BPI is guesstimated by
|
|
// BranchProbabilityAnalysis. The injector (this pass) only persists whatever
|
|
// information the analysis provides, in other words, the pass being tested
|
|
// will get the same BPI it does if the injector wasn't running.
|
|
auto &BPI = FAM.getResult<BranchProbabilityAnalysis>(F);
|
|
|
|
// Inject a function count if there's none. It's reasonable for a pass to
|
|
// want to clear the MD_prof of a function with zero entry count. If the
|
|
// original profile (iFDO or AFDO) is empty for a function, it's simpler to
|
|
// require assigning it the 0-entry count explicitly than to mark every branch
|
|
// as cold (we do want some explicit information in the spirit of what this
|
|
// verifier wants to achieve - make dropping / corrupting MD_prof
|
|
// unit-testable)
|
|
if (!F.getEntryCount(/*AllowSynthetic=*/true))
|
|
F.setEntryCount(DefaultFunctionEntryCount);
|
|
// If there is an entry count that's 0, then don't bother injecting. We won't
|
|
// verify these either.
|
|
if (F.getEntryCount(/*AllowSynthetic=*/true)->getCount() == 0)
|
|
return false;
|
|
bool Changed = false;
|
|
for (auto &BB : F) {
|
|
auto *Term = getTerminatorBenefitingFromMDProf(BB);
|
|
if (!Term || Term->getMetadata(LLVMContext::MD_prof))
|
|
continue;
|
|
SmallVector<BranchProbability> Probs;
|
|
Probs.reserve(Term->getNumSuccessors());
|
|
for (auto I = 0U, E = Term->getNumSuccessors(); I < E; ++I)
|
|
Probs.emplace_back(BPI.getEdgeProbability(&BB, Term->getSuccessor(I)));
|
|
|
|
assert(llvm::find_if(Probs,
|
|
[](const BranchProbability &P) {
|
|
return P.isUnknown();
|
|
}) == Probs.end() &&
|
|
"All branch probabilities should be valid");
|
|
const auto *FirstZeroDenominator =
|
|
find_if(Probs, [](const BranchProbability &P) {
|
|
return P.getDenominator() == 0;
|
|
});
|
|
(void)FirstZeroDenominator;
|
|
assert(FirstZeroDenominator == Probs.end());
|
|
const auto *FirstNonZeroNumerator =
|
|
find_if(Probs, [](const BranchProbability &P) { return !P.isZero(); });
|
|
assert(FirstNonZeroNumerator != Probs.end());
|
|
DynamicAPInt LCM(Probs[0].getDenominator());
|
|
DynamicAPInt GCD(FirstNonZeroNumerator->getNumerator());
|
|
for (const auto &Prob : drop_begin(Probs)) {
|
|
if (!Prob.getNumerator())
|
|
continue;
|
|
LCM = llvm::lcm(LCM, DynamicAPInt(Prob.getDenominator()));
|
|
GCD = llvm::gcd(GCD, DynamicAPInt(Prob.getNumerator()));
|
|
}
|
|
SmallVector<uint32_t> Weights;
|
|
Weights.reserve(Term->getNumSuccessors());
|
|
for (const auto &Prob : Probs) {
|
|
DynamicAPInt W =
|
|
(Prob.getNumerator() * LCM / GCD) / Prob.getDenominator();
|
|
Weights.emplace_back(static_cast<uint32_t>((int64_t)W));
|
|
}
|
|
setBranchWeights(*Term, Weights, /*IsExpected=*/false);
|
|
Changed = true;
|
|
}
|
|
return Changed;
|
|
}
|
|
|
|
PreservedAnalyses ProfileInjectorPass::run(Function &F,
|
|
FunctionAnalysisManager &FAM) {
|
|
ProfileInjector PI(F, FAM);
|
|
if (!PI.inject())
|
|
return PreservedAnalyses::all();
|
|
|
|
return PreservedAnalyses::none();
|
|
}
|
|
|
|
PreservedAnalyses ProfileVerifierPass::run(Function &F,
|
|
FunctionAnalysisManager &FAM) {
|
|
const auto EntryCount = F.getEntryCount(/*AllowSynthetic=*/true);
|
|
if (!EntryCount) {
|
|
F.getContext().emitError("Profile verification failed: function entry "
|
|
"count missing (set to 0 if cold)");
|
|
return PreservedAnalyses::all();
|
|
}
|
|
if (EntryCount->getCount() == 0)
|
|
return PreservedAnalyses::all();
|
|
for (const auto &BB : F)
|
|
if (const auto *Term =
|
|
ProfileInjector::getTerminatorBenefitingFromMDProf(BB))
|
|
if (!Term->getMetadata(LLVMContext::MD_prof))
|
|
F.getContext().emitError(
|
|
"Profile verification failed: branch annotation missing");
|
|
|
|
return PreservedAnalyses::all();
|
|
}
|