Skip to content

Commit a6a6fca

Browse files
authored
[ubsan][pgo] Pass to remove ubsan checks based on profile data (#83471)
UBSAN checks can be too expensive to be used in release binaries. However not all code affect performace in the same way. Removing small number of checks in hot code we can performance loss, preserving most of the checks.
1 parent 4157217 commit a6a6fca

File tree

6 files changed

+536
-0
lines changed

6 files changed

+536
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===- RemoveTrapsPass.h ----------------------------------------*- 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-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
/// \file
9+
/// This file provides the interface for the pass responsible for removing
10+
/// expensive ubsan checks.
11+
///
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_UBSANOPTIMIZATIONPASS_H
15+
#define LLVM_TRANSFORMS_INSTRUMENTATION_UBSANOPTIMIZATIONPASS_H
16+
17+
#include "llvm/IR/Function.h"
18+
#include "llvm/IR/PassManager.h"
19+
#include "llvm/Pass.h"
20+
21+
namespace llvm {
22+
23+
// This pass is responsible for removing optional traps, like llvm.ubsantrap
24+
// from the hot code.
25+
class RemoveTrapsPass : public PassInfoMixin<RemoveTrapsPass> {
26+
public:
27+
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
28+
};
29+
30+
} // namespace llvm
31+
32+
#endif

llvm/lib/Passes/PassBuilder.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@
177177
#include "llvm/Transforms/Instrumentation/PGOForceFunctionAttrs.h"
178178
#include "llvm/Transforms/Instrumentation/PGOInstrumentation.h"
179179
#include "llvm/Transforms/Instrumentation/PoisonChecking.h"
180+
#include "llvm/Transforms/Instrumentation/RemoveTrapsPass.h"
180181
#include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h"
181182
#include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
182183
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"

llvm/lib/Passes/PassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,7 @@ FUNCTION_PASS("print<uniformity>", UniformityInfoPrinterPass(dbgs()))
422422
FUNCTION_PASS("reassociate", ReassociatePass())
423423
FUNCTION_PASS("redundant-dbg-inst-elim", RedundantDbgInstEliminationPass())
424424
FUNCTION_PASS("reg2mem", RegToMemPass())
425+
FUNCTION_PASS("remove-traps", RemoveTrapsPass())
425426
FUNCTION_PASS("safe-stack", SafeStackPass(TM))
426427
FUNCTION_PASS("scalarize-masked-mem-intrin", ScalarizeMaskedMemIntrinPass())
427428
FUNCTION_PASS("scalarizer", ScalarizerPass())

llvm/lib/Transforms/Instrumentation/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ add_llvm_component_library(LLVMInstrumentation
1717
PGOInstrumentation.cpp
1818
PGOMemOPSizeOpt.cpp
1919
PoisonChecking.cpp
20+
RemoveTrapsPass.cpp
2021
SanitizerCoverage.cpp
2122
SanitizerBinaryMetadata.cpp
2223
ValueProfileCollector.cpp
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
//===- RemoveTrapsPass.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-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/Transforms/Instrumentation/RemoveTrapsPass.h"
10+
11+
#include "llvm/ADT/SmallVector.h"
12+
#include "llvm/ADT/Statistic.h"
13+
#include "llvm/Analysis/ProfileSummaryInfo.h"
14+
#include "llvm/IR/Instructions.h"
15+
#include "llvm/IR/IntrinsicInst.h"
16+
#include "llvm/IR/Intrinsics.h"
17+
#include "llvm/Support/RandomNumberGenerator.h"
18+
#include <memory>
19+
#include <random>
20+
21+
using namespace llvm;
22+
23+
#define DEBUG_TYPE "remove-traps"
24+
25+
static cl::opt<int> HotPercentileCutoff(
26+
"remove-traps-percentile-cutoff-hot", cl::init(0),
27+
cl::desc("Alternative hot percentile cuttoff. By default "
28+
"`-profile-summary-cutoff-hot` is used."));
29+
30+
static cl::opt<float>
31+
RandomRate("remove-traps-random-rate", cl::init(0.0),
32+
cl::desc("Probability value in the range [0.0, 1.0] of "
33+
"unconditional pseudo-random checks removal."));
34+
35+
STATISTIC(NumChecksTotal, "Number of checks");
36+
STATISTIC(NumChecksRemoved, "Number of removed checks");
37+
38+
static bool removeUbsanTraps(Function &F, const BlockFrequencyInfo &BFI,
39+
const ProfileSummaryInfo *PSI) {
40+
SmallVector<IntrinsicInst *, 16> Remove;
41+
std::unique_ptr<RandomNumberGenerator> Rng;
42+
43+
auto ShouldRemove = [&](bool IsHot) {
44+
if (!RandomRate.getNumOccurrences())
45+
return IsHot;
46+
if (!Rng)
47+
Rng = F.getParent()->createRNG(F.getName());
48+
std::bernoulli_distribution D(RandomRate);
49+
return D(*Rng);
50+
};
51+
52+
for (BasicBlock &BB : F) {
53+
for (Instruction &I : BB) {
54+
IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I);
55+
if (!II)
56+
continue;
57+
auto ID = II->getIntrinsicID();
58+
switch (ID) {
59+
case Intrinsic::ubsantrap: {
60+
++NumChecksTotal;
61+
62+
bool IsHot = false;
63+
if (PSI) {
64+
uint64_t Count = 0;
65+
for (const auto *PR : predecessors(&BB))
66+
Count += BFI.getBlockProfileCount(PR).value_or(0);
67+
68+
IsHot =
69+
HotPercentileCutoff.getNumOccurrences()
70+
? (HotPercentileCutoff > 0 &&
71+
PSI->isHotCountNthPercentile(HotPercentileCutoff, Count))
72+
: PSI->isHotCount(Count);
73+
}
74+
75+
if (ShouldRemove(IsHot)) {
76+
Remove.push_back(II);
77+
++NumChecksRemoved;
78+
}
79+
break;
80+
}
81+
default:
82+
break;
83+
}
84+
}
85+
}
86+
87+
for (IntrinsicInst *I : Remove)
88+
I->eraseFromParent();
89+
90+
return !Remove.empty();
91+
}
92+
93+
PreservedAnalyses RemoveTrapsPass::run(Function &F,
94+
FunctionAnalysisManager &AM) {
95+
if (F.isDeclaration())
96+
return PreservedAnalyses::all();
97+
auto &MAMProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
98+
ProfileSummaryInfo *PSI =
99+
MAMProxy.getCachedResult<ProfileSummaryAnalysis>(*F.getParent());
100+
BlockFrequencyInfo &BFI = AM.getResult<BlockFrequencyAnalysis>(F);
101+
102+
return removeUbsanTraps(F, BFI, PSI) ? PreservedAnalyses::none()
103+
: PreservedAnalyses::all();
104+
}

0 commit comments

Comments
 (0)