Adding 2 passes, one to inject `MD_prof` and one to check its presence. A subsequent patch will add these (similar to debugify) to `opt` (and, eventually, a variant of this, to `llc`) Tracking issue: #147390
130 lines
4.8 KiB
C++
130 lines
4.8 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"
|
|
|
|
using namespace llvm;
|
|
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);
|
|
|
|
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) {
|
|
for (const auto &BB : F)
|
|
if (const auto *Term =
|
|
ProfileInjector::getTerminatorBenefitingFromMDProf(BB))
|
|
if (!Term->getMetadata(LLVMContext::MD_prof))
|
|
F.getContext().emitError("Profile verification failed");
|
|
|
|
return PreservedAnalyses::none();
|
|
}
|