Skip to content

[ubsan] Parse and use <cutoffs[0,1,2]=70000;cutoffs[5,6,8]=90000> in LowerAllowCheckPass #124211

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jan 28, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@ namespace llvm {
class LowerAllowCheckPass : public PassInfoMixin<LowerAllowCheckPass> {
public:
struct Options {
std::vector<unsigned int> placeholder; // TODO: cutoffs
std::vector<unsigned int> cutoffs;
};

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

static bool IsRequested();
void printPipeline(raw_ostream &OS,
function_ref<StringRef(StringRef)> MapClassName2PassName);

private:
LowerAllowCheckPass::Options Opts;
Expand Down
62 changes: 58 additions & 4 deletions llvm/lib/Passes/PassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -828,11 +828,65 @@ parseLowerAllowCheckPassOptions(StringRef Params) {
StringRef ParamName;
std::tie(ParamName, Params) = Params.split(';');

return make_error<StringError>(
formatv("invalid LowerAllowCheck pass parameter '{0}' ", ParamName)
.str(),
inconvertibleErrorCode());
// Format is <cutoffs[1,2,3]=70000;cutoffs[5,6,8]=90000>
//
// Parsing allows duplicate indices (last one takes precedence).
// It would technically be in spec to specify
// cutoffs[0]=70000,cutoffs[1]=90000,cutoffs[0]=80000,...
if (ParamName.starts_with("cutoffs[")) {
StringRef IndicesStr;
StringRef CutoffStr;

std::tie(IndicesStr, CutoffStr) = ParamName.split("]=");
// cutoffs[1,2,3
// 70000

int cutoff;
if (CutoffStr.getAsInteger(0, cutoff))
return make_error<StringError>(
formatv("invalid LowerAllowCheck pass cutoffs parameter '{0}' "
"({1})",
CutoffStr, Params)
.str(),
inconvertibleErrorCode());

if (!IndicesStr.consume_front("cutoffs[") || IndicesStr == "")
return make_error<StringError>(
formatv("invalid LowerAllowCheck pass index parameter '{0}' "
"({1})",
IndicesStr, CutoffStr)
.str(),
inconvertibleErrorCode());

while (IndicesStr != "") {
StringRef firstIndexStr;
std::tie(firstIndexStr, IndicesStr) = IndicesStr.split('|');

unsigned int index;
if (firstIndexStr.getAsInteger(0, index))
return make_error<StringError>(
formatv("invalid LowerAllowCheck pass index parameter '{0}' "
"({1}) {2}",
firstIndexStr, IndicesStr)
.str(),
inconvertibleErrorCode());

// In the common case (sequentially increasing indices), we will issue
// O(n) resize requests. We assume the underlying data structure has
// O(1) runtime for each added element.
if (index >= Result.cutoffs.size())
Result.cutoffs.resize(index + 1, 0);

Result.cutoffs[index] = cutoff;
}
} else {
return make_error<StringError>(
formatv("invalid LowerAllowCheck pass parameter '{0}' ", ParamName)
.str(),
inconvertibleErrorCode());
}
}

return Result;
}

Expand Down
62 changes: 52 additions & 10 deletions llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/RandomNumberGenerator.h"
#include <memory>
#include <random>
Expand Down Expand Up @@ -71,7 +72,8 @@ static void emitRemark(IntrinsicInst *II, OptimizationRemarkEmitter &ORE,

static bool removeUbsanTraps(Function &F, const BlockFrequencyInfo &BFI,
const ProfileSummaryInfo *PSI,
OptimizationRemarkEmitter &ORE) {
OptimizationRemarkEmitter &ORE,
const std::vector<unsigned int> &cutoffs) {
SmallVector<std::pair<IntrinsicInst *, bool>, 16> ReplaceWithValue;
std::unique_ptr<RandomNumberGenerator> Rng;

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

auto ShouldRemoveHot = [&](const BasicBlock &BB) {
return HotPercentileCutoff.getNumOccurrences() && PSI &&
PSI->isHotCountNthPercentile(
HotPercentileCutoff, BFI.getBlockProfileCount(&BB).value_or(0));
auto GetCutoff = [&](const IntrinsicInst *II) -> unsigned {
if (HotPercentileCutoff.getNumOccurrences())
return HotPercentileCutoff;
else if (II->getIntrinsicID() == Intrinsic::allow_ubsan_check) {
auto *Kind = cast<ConstantInt>(II->getArgOperand(0));
if (Kind->getZExtValue() < cutoffs.size())
return cutoffs[Kind->getZExtValue()];
}

return 0;
};

auto ShouldRemoveHot = [&](const BasicBlock &BB, unsigned int cutoff) {
return (cutoff == 1000000) ||
(PSI && PSI->isHotCountNthPercentile(
cutoff, BFI.getBlockProfileCount(&BB).value_or(0)));
};

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

auto ShouldRemove = [&](const BasicBlock &BB) {
return ShouldRemoveRandom() || ShouldRemoveHot(BB);
auto ShouldRemove = [&](const IntrinsicInst *II) {
unsigned int cutoff = GetCutoff(II);
return ShouldRemoveRandom() || ShouldRemoveHot(*(II->getParent()), cutoff);
};

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

bool ToRemove = ShouldRemove(BB);
bool ToRemove = ShouldRemove(II);

ReplaceWithValue.push_back({
II,
ToRemove,
Expand Down Expand Up @@ -142,11 +158,37 @@ PreservedAnalyses LowerAllowCheckPass::run(Function &F,
OptimizationRemarkEmitter &ORE =
AM.getResult<OptimizationRemarkEmitterAnalysis>(F);

return removeUbsanTraps(F, BFI, PSI, ORE) ? PreservedAnalyses::none()
: PreservedAnalyses::all();
return removeUbsanTraps(F, BFI, PSI, ORE, Opts.cutoffs)
? PreservedAnalyses::none()
: PreservedAnalyses::all();
}

bool LowerAllowCheckPass::IsRequested() {
return RandomRate.getNumOccurrences() ||
HotPercentileCutoff.getNumOccurrences();
}

void LowerAllowCheckPass::printPipeline(
raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
static_cast<PassInfoMixin<LowerAllowCheckPass> *>(this)->printPipeline(
OS, MapClassName2PassName);
OS << "<";

// Format is <cutoffs[0,1,2]=70000;cutoffs[5,6,8]=90000>
// but it's equally valid to specify
// cutoffs[0]=70000;cutoffs[1]=70000;cutoffs[2]=70000;cutoffs[5]=90000;...
// and that's what we do here. It is verbose but valid and easy to verify
// correctness.
// TODO: print shorter output by combining adjacent runs, etc.
int i = 0;
for (unsigned int cutoff : Opts.cutoffs) {
if (cutoff > 0) {
if (i > 0)
OS << ";";
OS << "cutoffs[" << i << "]=" << cutoff;
}

i++;
}
OS << '>';
}
8 changes: 8 additions & 0 deletions llvm/test/Transforms/lower-builtin-allow-check.ll
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
; RUN: opt < %s -passes='function(lower-allow-check)' -S | FileCheck %s --check-prefixes=NOPROFILE
; RUN: opt < %s -passes='function(lower-allow-check)' -lower-allow-check-random-rate=0 -S | FileCheck %s --check-prefixes=NONE
; RUN: opt < %s -passes='function(lower-allow-check)' -lower-allow-check-random-rate=1 -S | FileCheck %s --check-prefixes=ALL
;
; RUN: opt < %s -passes='require<profile-summary>,function(lower-allow-check<cutoffs[22]=990000>)' -S | FileCheck %s --check-prefixes=HOT99
; RUN: opt < %s -passes='require<profile-summary>,function(lower-allow-check<cutoffs[22]=700000>)' -S | FileCheck %s --check-prefixes=HOT70
; 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
; 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
;
; -lower-allow-check-percentile-cutoff is deprecated and will be removed in the future;
; use the cutoffs parameter to the lower-allow-check pass, as shown above.
; RUN: opt < %s -passes='require<profile-summary>,function(lower-allow-check)' -lower-allow-check-percentile-cutoff-hot=990000 -S | FileCheck %s --check-prefixes=HOT99
; RUN: opt < %s -passes='require<profile-summary>,function(lower-allow-check)' -lower-allow-check-percentile-cutoff-hot=700000 -S | FileCheck %s --check-prefixes=HOT70
; 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
Expand Down
Loading