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