xref: /llvm-project/llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp (revision fa9ac62d02fd6b5028f301ee398c3d3a1c0eacae)
149f0b536SVitaly Buka //===- LowerAllowCheckPass.cpp ----------------------------------*- C++ -*-===//
249f0b536SVitaly Buka //
349f0b536SVitaly Buka // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
449f0b536SVitaly Buka // See https://llvm.org/LICENSE.txt for license information.
549f0b536SVitaly Buka // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
649f0b536SVitaly Buka //
749f0b536SVitaly Buka //===----------------------------------------------------------------------===//
849f0b536SVitaly Buka 
949f0b536SVitaly Buka #include "llvm/Transforms/Instrumentation/LowerAllowCheckPass.h"
1049f0b536SVitaly Buka 
1149f0b536SVitaly Buka #include "llvm/ADT/SmallVector.h"
1249f0b536SVitaly Buka #include "llvm/ADT/Statistic.h"
13d927d186SVitaly Buka #include "llvm/ADT/StringRef.h"
14d927d186SVitaly Buka #include "llvm/Analysis/OptimizationRemarkEmitter.h"
1549f0b536SVitaly Buka #include "llvm/Analysis/ProfileSummaryInfo.h"
16d927d186SVitaly Buka #include "llvm/IR/Constants.h"
17d927d186SVitaly Buka #include "llvm/IR/DiagnosticInfo.h"
1849f0b536SVitaly Buka #include "llvm/IR/Instructions.h"
1949f0b536SVitaly Buka #include "llvm/IR/IntrinsicInst.h"
2049f0b536SVitaly Buka #include "llvm/IR/Intrinsics.h"
21d927d186SVitaly Buka #include "llvm/IR/Metadata.h"
224169338eSNikita Popov #include "llvm/IR/Module.h"
23*fa9ac62dSThurston Dang #include "llvm/Support/Debug.h"
2449f0b536SVitaly Buka #include "llvm/Support/RandomNumberGenerator.h"
2549f0b536SVitaly Buka #include <memory>
2649f0b536SVitaly Buka #include <random>
2749f0b536SVitaly Buka 
2849f0b536SVitaly Buka using namespace llvm;
2949f0b536SVitaly Buka 
3049f0b536SVitaly Buka #define DEBUG_TYPE "lower-allow-check"
3149f0b536SVitaly Buka 
3249f0b536SVitaly Buka static cl::opt<int>
3349f0b536SVitaly Buka     HotPercentileCutoff("lower-allow-check-percentile-cutoff-hot",
3467efbd0bSRyan Mansfield                         cl::desc("Hot percentile cutoff."));
3549f0b536SVitaly Buka 
3649f0b536SVitaly Buka static cl::opt<float>
3749f0b536SVitaly Buka     RandomRate("lower-allow-check-random-rate",
3849f0b536SVitaly Buka                cl::desc("Probability value in the range [0.0, 1.0] of "
3906463440SVitaly Buka                         "unconditional pseudo-random checks."));
4049f0b536SVitaly Buka 
4149f0b536SVitaly Buka STATISTIC(NumChecksTotal, "Number of checks");
4249f0b536SVitaly Buka STATISTIC(NumChecksRemoved, "Number of removed checks");
4349f0b536SVitaly Buka 
44d927d186SVitaly Buka struct RemarkInfo {
45d927d186SVitaly Buka   ore::NV Kind;
46d927d186SVitaly Buka   ore::NV F;
47d927d186SVitaly Buka   ore::NV BB;
48d927d186SVitaly Buka   explicit RemarkInfo(IntrinsicInst *II)
49d927d186SVitaly Buka       : Kind("Kind", II->getArgOperand(0)),
50d927d186SVitaly Buka         F("Function", II->getParent()->getParent()),
51d927d186SVitaly Buka         BB("Block", II->getParent()->getName()) {}
52d927d186SVitaly Buka };
53d927d186SVitaly Buka 
54d927d186SVitaly Buka static void emitRemark(IntrinsicInst *II, OptimizationRemarkEmitter &ORE,
55d927d186SVitaly Buka                        bool Removed) {
56d927d186SVitaly Buka   if (Removed) {
57d927d186SVitaly Buka     ORE.emit([&]() {
58d927d186SVitaly Buka       RemarkInfo Info(II);
59d927d186SVitaly Buka       return OptimizationRemark(DEBUG_TYPE, "Removed", II)
60d927d186SVitaly Buka              << "Removed check: Kind=" << Info.Kind << " F=" << Info.F
61d927d186SVitaly Buka              << " BB=" << Info.BB;
62d927d186SVitaly Buka     });
63d927d186SVitaly Buka   } else {
64d927d186SVitaly Buka     ORE.emit([&]() {
65d927d186SVitaly Buka       RemarkInfo Info(II);
66d927d186SVitaly Buka       return OptimizationRemarkMissed(DEBUG_TYPE, "Allowed", II)
67d927d186SVitaly Buka              << "Allowed check: Kind=" << Info.Kind << " F=" << Info.F
68d927d186SVitaly Buka              << " BB=" << Info.BB;
69d927d186SVitaly Buka     });
70d927d186SVitaly Buka   }
71d927d186SVitaly Buka }
72d927d186SVitaly Buka 
7349f0b536SVitaly Buka static bool removeUbsanTraps(Function &F, const BlockFrequencyInfo &BFI,
74d927d186SVitaly Buka                              const ProfileSummaryInfo *PSI,
75*fa9ac62dSThurston Dang                              OptimizationRemarkEmitter &ORE,
76*fa9ac62dSThurston Dang                              const std::vector<unsigned int> &cutoffs) {
7749f0b536SVitaly Buka   SmallVector<std::pair<IntrinsicInst *, bool>, 16> ReplaceWithValue;
7849f0b536SVitaly Buka   std::unique_ptr<RandomNumberGenerator> Rng;
7949f0b536SVitaly Buka 
8009542052SVitaly Buka   auto GetRng = [&]() -> RandomNumberGenerator & {
8149f0b536SVitaly Buka     if (!Rng)
8249f0b536SVitaly Buka       Rng = F.getParent()->createRNG(F.getName());
8309542052SVitaly Buka     return *Rng;
8409542052SVitaly Buka   };
8509542052SVitaly Buka 
86*fa9ac62dSThurston Dang   auto GetCutoff = [&](const IntrinsicInst *II) -> unsigned {
87*fa9ac62dSThurston Dang     if (HotPercentileCutoff.getNumOccurrences())
88*fa9ac62dSThurston Dang       return HotPercentileCutoff;
89*fa9ac62dSThurston Dang     else if (II->getIntrinsicID() == Intrinsic::allow_ubsan_check) {
90*fa9ac62dSThurston Dang       auto *Kind = cast<ConstantInt>(II->getArgOperand(0));
91*fa9ac62dSThurston Dang       if (Kind->getZExtValue() < cutoffs.size())
92*fa9ac62dSThurston Dang         return cutoffs[Kind->getZExtValue()];
93*fa9ac62dSThurston Dang     }
94*fa9ac62dSThurston Dang 
95*fa9ac62dSThurston Dang     return 0;
96*fa9ac62dSThurston Dang   };
97*fa9ac62dSThurston Dang 
98*fa9ac62dSThurston Dang   auto ShouldRemoveHot = [&](const BasicBlock &BB, unsigned int cutoff) {
99*fa9ac62dSThurston Dang     return (cutoff == 1000000) ||
100*fa9ac62dSThurston Dang            (PSI && PSI->isHotCountNthPercentile(
101*fa9ac62dSThurston Dang                        cutoff, BFI.getBlockProfileCount(&BB).value_or(0)));
10209542052SVitaly Buka   };
10309542052SVitaly Buka 
10409542052SVitaly Buka   auto ShouldRemoveRandom = [&]() {
10509542052SVitaly Buka     return RandomRate.getNumOccurrences() &&
10609542052SVitaly Buka            !std::bernoulli_distribution(RandomRate)(GetRng());
10709542052SVitaly Buka   };
10809542052SVitaly Buka 
109*fa9ac62dSThurston Dang   auto ShouldRemove = [&](const IntrinsicInst *II) {
110*fa9ac62dSThurston Dang     unsigned int cutoff = GetCutoff(II);
111*fa9ac62dSThurston Dang     return ShouldRemoveRandom() || ShouldRemoveHot(*(II->getParent()), cutoff);
11249f0b536SVitaly Buka   };
11349f0b536SVitaly Buka 
11449f0b536SVitaly Buka   for (BasicBlock &BB : F) {
11549f0b536SVitaly Buka     for (Instruction &I : BB) {
11649f0b536SVitaly Buka       IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I);
11749f0b536SVitaly Buka       if (!II)
11849f0b536SVitaly Buka         continue;
11949f0b536SVitaly Buka       auto ID = II->getIntrinsicID();
12049f0b536SVitaly Buka       switch (ID) {
12149f0b536SVitaly Buka       case Intrinsic::allow_ubsan_check:
12249f0b536SVitaly Buka       case Intrinsic::allow_runtime_check: {
12349f0b536SVitaly Buka         ++NumChecksTotal;
12449f0b536SVitaly Buka 
125*fa9ac62dSThurston Dang         bool ToRemove = ShouldRemove(II);
126*fa9ac62dSThurston Dang 
12749f0b536SVitaly Buka         ReplaceWithValue.push_back({
12849f0b536SVitaly Buka             II,
12949f0b536SVitaly Buka             ToRemove,
13049f0b536SVitaly Buka         });
13149f0b536SVitaly Buka         if (ToRemove)
13249f0b536SVitaly Buka           ++NumChecksRemoved;
133d927d186SVitaly Buka         emitRemark(II, ORE, ToRemove);
13449f0b536SVitaly Buka         break;
13549f0b536SVitaly Buka       }
13649f0b536SVitaly Buka       default:
13749f0b536SVitaly Buka         break;
13849f0b536SVitaly Buka       }
13949f0b536SVitaly Buka     }
14049f0b536SVitaly Buka   }
14149f0b536SVitaly Buka 
14249f0b536SVitaly Buka   for (auto [I, V] : ReplaceWithValue) {
14349f0b536SVitaly Buka     I->replaceAllUsesWith(ConstantInt::getBool(I->getType(), !V));
14449f0b536SVitaly Buka     I->eraseFromParent();
14549f0b536SVitaly Buka   }
14649f0b536SVitaly Buka 
14749f0b536SVitaly Buka   return !ReplaceWithValue.empty();
14849f0b536SVitaly Buka }
14949f0b536SVitaly Buka 
15049f0b536SVitaly Buka PreservedAnalyses LowerAllowCheckPass::run(Function &F,
15149f0b536SVitaly Buka                                            FunctionAnalysisManager &AM) {
15249f0b536SVitaly Buka   if (F.isDeclaration())
15349f0b536SVitaly Buka     return PreservedAnalyses::all();
15449f0b536SVitaly Buka   auto &MAMProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
15549f0b536SVitaly Buka   ProfileSummaryInfo *PSI =
15649f0b536SVitaly Buka       MAMProxy.getCachedResult<ProfileSummaryAnalysis>(*F.getParent());
15749f0b536SVitaly Buka   BlockFrequencyInfo &BFI = AM.getResult<BlockFrequencyAnalysis>(F);
158d927d186SVitaly Buka   OptimizationRemarkEmitter &ORE =
159d927d186SVitaly Buka       AM.getResult<OptimizationRemarkEmitterAnalysis>(F);
16049f0b536SVitaly Buka 
161*fa9ac62dSThurston Dang   return removeUbsanTraps(F, BFI, PSI, ORE, Opts.cutoffs)
162*fa9ac62dSThurston Dang              ? PreservedAnalyses::none()
16349f0b536SVitaly Buka              : PreservedAnalyses::all();
16449f0b536SVitaly Buka }
16549f0b536SVitaly Buka 
16649f0b536SVitaly Buka bool LowerAllowCheckPass::IsRequested() {
16749f0b536SVitaly Buka   return RandomRate.getNumOccurrences() ||
16849f0b536SVitaly Buka          HotPercentileCutoff.getNumOccurrences();
16949f0b536SVitaly Buka }
170*fa9ac62dSThurston Dang 
171*fa9ac62dSThurston Dang void LowerAllowCheckPass::printPipeline(
172*fa9ac62dSThurston Dang     raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
173*fa9ac62dSThurston Dang   static_cast<PassInfoMixin<LowerAllowCheckPass> *>(this)->printPipeline(
174*fa9ac62dSThurston Dang       OS, MapClassName2PassName);
175*fa9ac62dSThurston Dang   OS << "<";
176*fa9ac62dSThurston Dang 
177*fa9ac62dSThurston Dang   // Format is <cutoffs[0,1,2]=70000;cutoffs[5,6,8]=90000>
178*fa9ac62dSThurston Dang   // but it's equally valid to specify
179*fa9ac62dSThurston Dang   //   cutoffs[0]=70000;cutoffs[1]=70000;cutoffs[2]=70000;cutoffs[5]=90000;...
180*fa9ac62dSThurston Dang   // and that's what we do here. It is verbose but valid and easy to verify
181*fa9ac62dSThurston Dang   // correctness.
182*fa9ac62dSThurston Dang   // TODO: print shorter output by combining adjacent runs, etc.
183*fa9ac62dSThurston Dang   int i = 0;
184*fa9ac62dSThurston Dang   for (unsigned int cutoff : Opts.cutoffs) {
185*fa9ac62dSThurston Dang     if (cutoff > 0) {
186*fa9ac62dSThurston Dang       if (i > 0)
187*fa9ac62dSThurston Dang         OS << ";";
188*fa9ac62dSThurston Dang       OS << "cutoffs[" << i << "]=" << cutoff;
189*fa9ac62dSThurston Dang     }
190*fa9ac62dSThurston Dang 
191*fa9ac62dSThurston Dang     i++;
192*fa9ac62dSThurston Dang   }
193*fa9ac62dSThurston Dang   OS << '>';
194*fa9ac62dSThurston Dang }
195