199 lines · cpp
1//===- LowerAllowCheckPass.cpp ----------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/Transforms/Instrumentation/LowerAllowCheckPass.h"10 11#include "llvm/ADT/SmallVector.h"12#include "llvm/ADT/Statistic.h"13#include "llvm/ADT/StringExtras.h"14#include "llvm/ADT/StringRef.h"15#include "llvm/Analysis/OptimizationRemarkEmitter.h"16#include "llvm/Analysis/ProfileSummaryInfo.h"17#include "llvm/IR/Constants.h"18#include "llvm/IR/DiagnosticInfo.h"19#include "llvm/IR/InstIterator.h"20#include "llvm/IR/Instructions.h"21#include "llvm/IR/IntrinsicInst.h"22#include "llvm/IR/Intrinsics.h"23#include "llvm/IR/Metadata.h"24#include "llvm/IR/Module.h"25#include "llvm/Support/Debug.h"26#include "llvm/Support/RandomNumberGenerator.h"27#include <memory>28#include <random>29 30using namespace llvm;31 32#define DEBUG_TYPE "lower-allow-check"33 34static cl::opt<int>35 HotPercentileCutoff("lower-allow-check-percentile-cutoff-hot",36 cl::desc("Hot percentile cutoff."));37 38static cl::opt<float>39 RandomRate("lower-allow-check-random-rate",40 cl::desc("Probability value in the range [0.0, 1.0] of "41 "unconditional pseudo-random checks."));42 43STATISTIC(NumChecksTotal, "Number of checks");44STATISTIC(NumChecksRemoved, "Number of removed checks");45 46struct RemarkInfo {47 ore::NV Kind;48 ore::NV F;49 ore::NV BB;50 explicit RemarkInfo(IntrinsicInst *II)51 : Kind("Kind", II->getArgOperand(0)),52 F("Function", II->getParent()->getParent()),53 BB("Block", II->getParent()->getName()) {}54};55 56static void emitRemark(IntrinsicInst *II, OptimizationRemarkEmitter &ORE,57 bool Removed) {58 if (Removed) {59 ORE.emit([&]() {60 RemarkInfo Info(II);61 return OptimizationRemark(DEBUG_TYPE, "Removed", II)62 << "Removed check: Kind=" << Info.Kind << " F=" << Info.F63 << " BB=" << Info.BB;64 });65 } else {66 ORE.emit([&]() {67 RemarkInfo Info(II);68 return OptimizationRemarkMissed(DEBUG_TYPE, "Allowed", II)69 << "Allowed check: Kind=" << Info.Kind << " F=" << Info.F70 << " BB=" << Info.BB;71 });72 }73}74 75static bool lowerAllowChecks(Function &F, const BlockFrequencyInfo &BFI,76 const ProfileSummaryInfo *PSI,77 OptimizationRemarkEmitter &ORE,78 const LowerAllowCheckPass::Options &Opts) {79 SmallVector<std::pair<IntrinsicInst *, bool>, 16> ReplaceWithValue;80 std::unique_ptr<RandomNumberGenerator> Rng;81 82 auto GetRng = [&]() -> RandomNumberGenerator & {83 if (!Rng)84 Rng = F.getParent()->createRNG(F.getName());85 return *Rng;86 };87 88 auto GetCutoff = [&](const IntrinsicInst *II) -> unsigned {89 if (HotPercentileCutoff.getNumOccurrences())90 return HotPercentileCutoff;91 else if (II->getIntrinsicID() == Intrinsic::allow_ubsan_check) {92 auto *Kind = cast<ConstantInt>(II->getArgOperand(0));93 if (Kind->getZExtValue() < Opts.cutoffs.size())94 return Opts.cutoffs[Kind->getZExtValue()];95 } else if (II->getIntrinsicID() == Intrinsic::allow_runtime_check) {96 return Opts.runtime_check;97 }98 99 return 0;100 };101 102 auto ShouldRemoveHot = [&](const BasicBlock &BB, unsigned int cutoff) {103 return (cutoff == 1000000) ||104 (PSI && PSI->isHotCountNthPercentile(105 cutoff, BFI.getBlockProfileCount(&BB).value_or(0)));106 };107 108 auto ShouldRemoveRandom = [&]() {109 return RandomRate.getNumOccurrences() &&110 !std::bernoulli_distribution(RandomRate)(GetRng());111 };112 113 auto ShouldRemove = [&](const IntrinsicInst *II) {114 unsigned int cutoff = GetCutoff(II);115 return ShouldRemoveRandom() || ShouldRemoveHot(*(II->getParent()), cutoff);116 };117 118 for (Instruction &I : instructions(F)) {119 IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I);120 if (!II)121 continue;122 auto ID = II->getIntrinsicID();123 switch (ID) {124 case Intrinsic::allow_ubsan_check:125 case Intrinsic::allow_runtime_check: {126 ++NumChecksTotal;127 128 bool ToRemove = ShouldRemove(II);129 130 ReplaceWithValue.push_back({131 II,132 ToRemove,133 });134 if (ToRemove)135 ++NumChecksRemoved;136 emitRemark(II, ORE, ToRemove);137 break;138 }139 default:140 break;141 }142 }143 144 for (auto [I, V] : ReplaceWithValue) {145 I->replaceAllUsesWith(ConstantInt::getBool(I->getType(), !V));146 I->eraseFromParent();147 }148 149 return !ReplaceWithValue.empty();150}151 152PreservedAnalyses LowerAllowCheckPass::run(Function &F,153 FunctionAnalysisManager &AM) {154 if (F.isDeclaration())155 return PreservedAnalyses::all();156 auto &MAMProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);157 ProfileSummaryInfo *PSI =158 MAMProxy.getCachedResult<ProfileSummaryAnalysis>(*F.getParent());159 BlockFrequencyInfo &BFI = AM.getResult<BlockFrequencyAnalysis>(F);160 OptimizationRemarkEmitter &ORE =161 AM.getResult<OptimizationRemarkEmitterAnalysis>(F);162 163 return lowerAllowChecks(F, BFI, PSI, ORE, Opts)164 // We do not change the CFG, we only replace the intrinsics with165 // true or false.166 ? PreservedAnalyses::none().preserveSet<CFGAnalyses>()167 : PreservedAnalyses::all();168}169 170bool LowerAllowCheckPass::IsRequested() {171 return RandomRate.getNumOccurrences() ||172 HotPercentileCutoff.getNumOccurrences();173}174 175void LowerAllowCheckPass::printPipeline(176 raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {177 static_cast<PassInfoMixin<LowerAllowCheckPass> *>(this)->printPipeline(178 OS, MapClassName2PassName);179 OS << "<";180 181 // Format is <cutoffs[0,1,2]=70000;cutoffs[5,6,8]=90000>182 // but it's equally valid to specify183 // cutoffs[0]=70000;cutoffs[1]=70000;cutoffs[2]=70000;cutoffs[5]=90000;...184 // and that's what we do here. It is verbose but valid and easy to verify185 // correctness.186 // TODO: print shorter output by combining adjacent runs, etc.187 int i = 0;188 ListSeparator LS(";");189 for (unsigned int cutoff : Opts.cutoffs) {190 if (cutoff > 0)191 OS << LS << "cutoffs[" << i << "]=" << cutoff;192 i++;193 }194 if (Opts.runtime_check)195 OS << LS << "runtime_check=" << Opts.runtime_check;196 197 OS << '>';198}199