Skip to content

Commit 4ead266

Browse files
committed
[ubsan][pgo] Pass to remove ubsan checks based on profile data
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 ccf0c8d commit 4ead266

File tree

6 files changed

+527
-0
lines changed

6 files changed

+527
-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: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
#include "llvm/Transforms/Instrumentation/RemoveTrapsPass.h"
13+
14+
#include "llvm/ADT/SmallVector.h"
15+
#include "llvm/ADT/Statistic.h"
16+
#include "llvm/Analysis/ProfileSummaryInfo.h"
17+
#include "llvm/IR/Instructions.h"
18+
#include "llvm/IR/IntrinsicInst.h"
19+
#include "llvm/IR/Intrinsics.h"
20+
#include <cstdint>
21+
22+
using namespace llvm;
23+
24+
#define DEBUG_TYPE "remove-traps"
25+
26+
static constexpr unsigned MaxRandomRate = 1000;
27+
28+
static cl::opt<int> HotPercentileCutoff(
29+
"remove-traps-percentile-cutoff-hot", cl::init(0),
30+
cl::desc("Alternative hot percentile cuttoff. By default "
31+
"`-profile-summary-cutoff-hot` is used."));
32+
static cl::opt<float> RandomRate(
33+
"remove-traps-random-rate", cl::init(0.0),
34+
cl::desc(
35+
"Probability to use for pseudorandom unconditional checks removal."));
36+
37+
STATISTIC(NumChecksTotal, "Number of checks");
38+
STATISTIC(NumChecksRemoved, "Number of removed checks");
39+
40+
static SmallVector<IntrinsicInst *, 16>
41+
removeUbsanTraps(Function &F, FunctionAnalysisManager &FAM,
42+
ProfileSummaryInfo *PSI) {
43+
SmallVector<IntrinsicInst *, 16> Remove;
44+
45+
if (F.isDeclaration())
46+
return {};
47+
48+
auto &BFI = FAM.getResult<BlockFrequencyAnalysis>(F);
49+
50+
int BBCounter = 0;
51+
for (BasicBlock &BB : F) {
52+
for (Instruction &I : BB) {
53+
IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I);
54+
if (!II)
55+
continue;
56+
auto ID = II->getIntrinsicID();
57+
if (ID != Intrinsic::ubsantrap)
58+
continue;
59+
++NumChecksTotal;
60+
61+
bool IsHot = false;
62+
if (PSI) {
63+
uint64_t Count = 0;
64+
for (const auto *PR : predecessors(&BB))
65+
Count += BFI.getBlockProfileCount(PR).value_or(0);
66+
67+
IsHot = HotPercentileCutoff.getNumOccurrences()
68+
? PSI->isHotCountNthPercentile(HotPercentileCutoff, Count)
69+
: PSI->isHotCount(Count);
70+
}
71+
72+
if ((IsHot) || ((F.getGUID() + BBCounter++) % MaxRandomRate) <
73+
RandomRate * RandomRate) {
74+
Remove.push_back(II);
75+
++NumChecksRemoved;
76+
}
77+
}
78+
}
79+
return Remove;
80+
}
81+
82+
PreservedAnalyses RemoveTrapsPass::run(Function &F,
83+
FunctionAnalysisManager &AM) {
84+
if (F.isDeclaration())
85+
return PreservedAnalyses::all();
86+
auto &MAMProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
87+
ProfileSummaryInfo *PSI =
88+
MAMProxy.getCachedResult<ProfileSummaryAnalysis>(*F.getParent());
89+
90+
auto Remove = removeUbsanTraps(F, AM, PSI);
91+
for (auto *I : Remove)
92+
I->eraseFromParent();
93+
94+
return Remove.empty() ? PreservedAnalyses::all() : PreservedAnalyses::none();
95+
}

0 commit comments

Comments
 (0)