Skip to content

Commit fa9ac62

Browse files
authored
[ubsan] Parse and use <cutoffs[0,1,2]=70000;cutoffs[5,6,8]=90000> in LowerAllowCheckPass (#124211)
This adds and utilizes a cutoffs parameter for LowerAllowCheckPass, via the Options parameter (introduced in #122994). Future work will connect -fsanitize-skip-hot-cutoff (introduced patch in #121619) in the clang frontend to the cutoffs parameter used here.
1 parent 5ab43c3 commit fa9ac62

File tree

4 files changed

+121
-15
lines changed

4 files changed

+121
-15
lines changed

llvm/include/llvm/Transforms/Instrumentation/LowerAllowCheckPass.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@ namespace llvm {
2525
class LowerAllowCheckPass : public PassInfoMixin<LowerAllowCheckPass> {
2626
public:
2727
struct Options {
28-
std::vector<unsigned int> placeholder; // TODO: cutoffs
28+
std::vector<unsigned int> cutoffs;
2929
};
3030

3131
explicit LowerAllowCheckPass(LowerAllowCheckPass::Options Opts)
3232
: Opts(std::move(Opts)) {};
3333
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
3434

3535
static bool IsRequested();
36+
void printPipeline(raw_ostream &OS,
37+
function_ref<StringRef(StringRef)> MapClassName2PassName);
3638

3739
private:
3840
LowerAllowCheckPass::Options Opts;

llvm/lib/Passes/PassBuilder.cpp

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -828,11 +828,65 @@ parseLowerAllowCheckPassOptions(StringRef Params) {
828828
StringRef ParamName;
829829
std::tie(ParamName, Params) = Params.split(';');
830830

831-
return make_error<StringError>(
832-
formatv("invalid LowerAllowCheck pass parameter '{0}' ", ParamName)
833-
.str(),
834-
inconvertibleErrorCode());
831+
// Format is <cutoffs[1,2,3]=70000;cutoffs[5,6,8]=90000>
832+
//
833+
// Parsing allows duplicate indices (last one takes precedence).
834+
// It would technically be in spec to specify
835+
// cutoffs[0]=70000,cutoffs[1]=90000,cutoffs[0]=80000,...
836+
if (ParamName.starts_with("cutoffs[")) {
837+
StringRef IndicesStr;
838+
StringRef CutoffStr;
839+
840+
std::tie(IndicesStr, CutoffStr) = ParamName.split("]=");
841+
// cutoffs[1,2,3
842+
// 70000
843+
844+
int cutoff;
845+
if (CutoffStr.getAsInteger(0, cutoff))
846+
return make_error<StringError>(
847+
formatv("invalid LowerAllowCheck pass cutoffs parameter '{0}' "
848+
"({1})",
849+
CutoffStr, Params)
850+
.str(),
851+
inconvertibleErrorCode());
852+
853+
if (!IndicesStr.consume_front("cutoffs[") || IndicesStr == "")
854+
return make_error<StringError>(
855+
formatv("invalid LowerAllowCheck pass index parameter '{0}' "
856+
"({1})",
857+
IndicesStr, CutoffStr)
858+
.str(),
859+
inconvertibleErrorCode());
860+
861+
while (IndicesStr != "") {
862+
StringRef firstIndexStr;
863+
std::tie(firstIndexStr, IndicesStr) = IndicesStr.split('|');
864+
865+
unsigned int index;
866+
if (firstIndexStr.getAsInteger(0, index))
867+
return make_error<StringError>(
868+
formatv("invalid LowerAllowCheck pass index parameter '{0}' "
869+
"({1}) {2}",
870+
firstIndexStr, IndicesStr)
871+
.str(),
872+
inconvertibleErrorCode());
873+
874+
// In the common case (sequentially increasing indices), we will issue
875+
// O(n) resize requests. We assume the underlying data structure has
876+
// O(1) runtime for each added element.
877+
if (index >= Result.cutoffs.size())
878+
Result.cutoffs.resize(index + 1, 0);
879+
880+
Result.cutoffs[index] = cutoff;
881+
}
882+
} else {
883+
return make_error<StringError>(
884+
formatv("invalid LowerAllowCheck pass parameter '{0}' ", ParamName)
885+
.str(),
886+
inconvertibleErrorCode());
887+
}
835888
}
889+
836890
return Result;
837891
}
838892

llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "llvm/IR/Intrinsics.h"
2121
#include "llvm/IR/Metadata.h"
2222
#include "llvm/IR/Module.h"
23+
#include "llvm/Support/Debug.h"
2324
#include "llvm/Support/RandomNumberGenerator.h"
2425
#include <memory>
2526
#include <random>
@@ -71,7 +72,8 @@ static void emitRemark(IntrinsicInst *II, OptimizationRemarkEmitter &ORE,
7172

7273
static bool removeUbsanTraps(Function &F, const BlockFrequencyInfo &BFI,
7374
const ProfileSummaryInfo *PSI,
74-
OptimizationRemarkEmitter &ORE) {
75+
OptimizationRemarkEmitter &ORE,
76+
const std::vector<unsigned int> &cutoffs) {
7577
SmallVector<std::pair<IntrinsicInst *, bool>, 16> ReplaceWithValue;
7678
std::unique_ptr<RandomNumberGenerator> Rng;
7779

@@ -81,19 +83,32 @@ static bool removeUbsanTraps(Function &F, const BlockFrequencyInfo &BFI,
8183
return *Rng;
8284
};
8385

84-
auto ShouldRemoveHot = [&](const BasicBlock &BB) {
85-
return HotPercentileCutoff.getNumOccurrences() && PSI &&
86-
PSI->isHotCountNthPercentile(
87-
HotPercentileCutoff, BFI.getBlockProfileCount(&BB).value_or(0));
86+
auto GetCutoff = [&](const IntrinsicInst *II) -> unsigned {
87+
if (HotPercentileCutoff.getNumOccurrences())
88+
return HotPercentileCutoff;
89+
else if (II->getIntrinsicID() == Intrinsic::allow_ubsan_check) {
90+
auto *Kind = cast<ConstantInt>(II->getArgOperand(0));
91+
if (Kind->getZExtValue() < cutoffs.size())
92+
return cutoffs[Kind->getZExtValue()];
93+
}
94+
95+
return 0;
96+
};
97+
98+
auto ShouldRemoveHot = [&](const BasicBlock &BB, unsigned int cutoff) {
99+
return (cutoff == 1000000) ||
100+
(PSI && PSI->isHotCountNthPercentile(
101+
cutoff, BFI.getBlockProfileCount(&BB).value_or(0)));
88102
};
89103

90104
auto ShouldRemoveRandom = [&]() {
91105
return RandomRate.getNumOccurrences() &&
92106
!std::bernoulli_distribution(RandomRate)(GetRng());
93107
};
94108

95-
auto ShouldRemove = [&](const BasicBlock &BB) {
96-
return ShouldRemoveRandom() || ShouldRemoveHot(BB);
109+
auto ShouldRemove = [&](const IntrinsicInst *II) {
110+
unsigned int cutoff = GetCutoff(II);
111+
return ShouldRemoveRandom() || ShouldRemoveHot(*(II->getParent()), cutoff);
97112
};
98113

99114
for (BasicBlock &BB : F) {
@@ -107,7 +122,8 @@ static bool removeUbsanTraps(Function &F, const BlockFrequencyInfo &BFI,
107122
case Intrinsic::allow_runtime_check: {
108123
++NumChecksTotal;
109124

110-
bool ToRemove = ShouldRemove(BB);
125+
bool ToRemove = ShouldRemove(II);
126+
111127
ReplaceWithValue.push_back({
112128
II,
113129
ToRemove,
@@ -142,11 +158,37 @@ PreservedAnalyses LowerAllowCheckPass::run(Function &F,
142158
OptimizationRemarkEmitter &ORE =
143159
AM.getResult<OptimizationRemarkEmitterAnalysis>(F);
144160

145-
return removeUbsanTraps(F, BFI, PSI, ORE) ? PreservedAnalyses::none()
146-
: PreservedAnalyses::all();
161+
return removeUbsanTraps(F, BFI, PSI, ORE, Opts.cutoffs)
162+
? PreservedAnalyses::none()
163+
: PreservedAnalyses::all();
147164
}
148165

149166
bool LowerAllowCheckPass::IsRequested() {
150167
return RandomRate.getNumOccurrences() ||
151168
HotPercentileCutoff.getNumOccurrences();
152169
}
170+
171+
void LowerAllowCheckPass::printPipeline(
172+
raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
173+
static_cast<PassInfoMixin<LowerAllowCheckPass> *>(this)->printPipeline(
174+
OS, MapClassName2PassName);
175+
OS << "<";
176+
177+
// Format is <cutoffs[0,1,2]=70000;cutoffs[5,6,8]=90000>
178+
// but it's equally valid to specify
179+
// cutoffs[0]=70000;cutoffs[1]=70000;cutoffs[2]=70000;cutoffs[5]=90000;...
180+
// and that's what we do here. It is verbose but valid and easy to verify
181+
// correctness.
182+
// TODO: print shorter output by combining adjacent runs, etc.
183+
int i = 0;
184+
for (unsigned int cutoff : Opts.cutoffs) {
185+
if (cutoff > 0) {
186+
if (i > 0)
187+
OS << ";";
188+
OS << "cutoffs[" << i << "]=" << cutoff;
189+
}
190+
191+
i++;
192+
}
193+
OS << '>';
194+
}

llvm/test/Transforms/lower-builtin-allow-check.ll

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
; RUN: opt < %s -passes='function(lower-allow-check)' -S | FileCheck %s --check-prefixes=NOPROFILE
33
; RUN: opt < %s -passes='function(lower-allow-check)' -lower-allow-check-random-rate=0 -S | FileCheck %s --check-prefixes=NONE
44
; RUN: opt < %s -passes='function(lower-allow-check)' -lower-allow-check-random-rate=1 -S | FileCheck %s --check-prefixes=ALL
5+
;
6+
; RUN: opt < %s -passes='require<profile-summary>,function(lower-allow-check<cutoffs[22]=990000>)' -S | FileCheck %s --check-prefixes=HOT99
7+
; RUN: opt < %s -passes='require<profile-summary>,function(lower-allow-check<cutoffs[22]=700000>)' -S | FileCheck %s --check-prefixes=HOT70
8+
; RUN: opt < %s -passes='require<profile-summary>,function(lower-allow-check<cutoffs[22]=990000>)' -lower-allow-check-random-rate=0 -S | FileCheck %s --check-prefixes=NONE99
9+
; RUN: opt < %s -passes='require<profile-summary>,function(lower-allow-check<cutoffs[22]=700000>)' -lower-allow-check-random-rate=1 -S | FileCheck %s --check-prefixes=ALL70
10+
;
11+
; -lower-allow-check-percentile-cutoff is deprecated and will be removed in the future;
12+
; use the cutoffs parameter to the lower-allow-check pass, as shown above.
513
; RUN: opt < %s -passes='require<profile-summary>,function(lower-allow-check)' -lower-allow-check-percentile-cutoff-hot=990000 -S | FileCheck %s --check-prefixes=HOT99
614
; RUN: opt < %s -passes='require<profile-summary>,function(lower-allow-check)' -lower-allow-check-percentile-cutoff-hot=700000 -S | FileCheck %s --check-prefixes=HOT70
715
; RUN: opt < %s -passes='require<profile-summary>,function(lower-allow-check)' -lower-allow-check-random-rate=0 -lower-allow-check-percentile-cutoff-hot=990000 -S | FileCheck %s --check-prefixes=NONE99

0 commit comments

Comments
 (0)