Skip to content

Commit 193d015

Browse files
committed
[𝘀𝗽𝗿] initial version
Created using spr 1.3.4
2 parents b2ea046 + 765b8b7 commit 193d015

File tree

11 files changed

+228
-44
lines changed

11 files changed

+228
-44
lines changed

clang/lib/CodeGen/BackendUtil.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@ using namespace llvm;
100100
namespace llvm {
101101
extern cl::opt<bool> PrintPipelinePasses;
102102

103-
cl::opt<bool> ClRemoveTraps("clang-remove-traps", cl::Optional,
104-
cl::desc("Insert remove-traps pass."),
105-
cl::init(false));
103+
static cl::opt<bool> ClRemoveTraps("clang-remove-traps", cl::Optional,
104+
cl::desc("Insert remove-traps pass."),
105+
cl::init(false));
106106

107107
// Experiment to move sanitizers earlier.
108108
static cl::opt<bool> ClSanitizeOnOptimizerEarlyEP(

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,14 @@ using namespace CodeGen;
5656
// Experiment to make sanitizers easier to debug
5757
static llvm::cl::opt<bool> ClSanitizeDebugDeoptimization(
5858
"ubsan-unique-traps", llvm::cl::Optional,
59-
llvm::cl::desc("Deoptimize traps for UBSAN so there is 1 trap per check"),
59+
llvm::cl::desc("Deoptimize traps for UBSAN so there is 1 trap per check."),
60+
llvm::cl::init(false));
61+
62+
// TODO: Introduce frontend options to enabled per sanitizers, similar to
63+
// `fsanitize-trap`.
64+
static llvm::cl::opt<bool> ClSanitizeExpHot(
65+
"ubsan-exp-hot", llvm::cl::Optional,
66+
llvm::cl::desc("Pass UBSAN checks if `llvm.experimental.hot()` is true."),
6067
llvm::cl::init(false));
6168

6269
//===--------------------------------------------------------------------===//
@@ -3805,6 +3812,12 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
38053812
SanitizerHandler CheckHandlerID) {
38063813
llvm::BasicBlock *Cont = createBasicBlock("cont");
38073814

3815+
if (ClSanitizeExpHot) {
3816+
Checked =
3817+
Builder.CreateOr(Checked, Builder.CreateCall(CGM.getIntrinsic(
3818+
llvm::Intrinsic::experimental_hot)));
3819+
}
3820+
38083821
// If we're optimizing, collapse all calls to trap down to just one per
38093822
// check-type per function to save on code size.
38103823
if ((int)TrapBBs.size() <= CheckHandlerID)

clang/test/CodeGen/remote-traps.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
// RUN: %clang_cc1 -O1 -emit-llvm -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow %s -o - | FileCheck %s
2-
// RUN: %clang_cc1 -O1 -emit-llvm -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -mllvm -clang-remove-traps -mllvm -remove-traps-random-rate=1 %s -o - | FileCheck %s --implicit-check-not="call void @llvm.ubsantrap" --check-prefixes=REMOVE
1+
// RUN: %clang_cc1 -O1 %s -o - -emit-llvm -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -mllvm -ubsan-exp-hot | FileCheck %s
2+
// RUN: %clang_cc1 -O1 %s -o - -emit-llvm -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -mllvm -ubsan-exp-hot -mllvm -clang-remove-traps -mllvm -remove-traps-random-rate=1 %s -o - | FileCheck %s --check-prefixes=REMOVE
3+
4+
#include <stdbool.h>
35

46
int test(int x) {
57
return x + 123;
@@ -12,4 +14,19 @@ int test(int x) {
1214
// CHECK-NEXT: unreachable
1315

1416
// REMOVE-LABEL: define {{.*}}i32 @test(
15-
// REMOVE: call { i32, i1 } @llvm.sadd.with.overflow.i32(
17+
// REMOVE: add i32 %x, 123
18+
// REMOVE-NEXT: ret i32
19+
20+
21+
bool experimental_hot() __asm("llvm.experimental.hot");
22+
23+
bool test_asm() {
24+
return experimental_hot();
25+
}
26+
27+
// CHECK-LABEL: define {{.*}}i1 @test_asm(
28+
// CHECK: [[R:%.*]] = tail call zeroext i1 @llvm.experimental.hot()
29+
// CHECK: ret i1 [[R]]
30+
31+
// REMOVE-LABEL: define {{.*}}i1 @test_asm(
32+
// REMOVE: ret i1 true

llvm/docs/LangRef.rst

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27553,7 +27553,7 @@ in example below:
2755327553
.. code-block:: text
2755427554

2755527555
%cond = call i1 @llvm.experimental.widenable.condition()
27556-
br i1 %cond, label %solution_1, label %solution_2
27556+
br i1 %cond, label %fast_path, label %slow_path
2755727557

2755827558
label %fast_path:
2755927559
; Apply memory-consuming but fast solution for a task.
@@ -27639,6 +27639,54 @@ constant `true`. However it is always correct to replace
2763927639
it with any other `i1` value. Any pass can
2764027640
freely do it if it can benefit from non-default lowering.
2764127641

27642+
'``llvm.experimental.hot``' Intrinsic
27643+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
27644+
27645+
Syntax:
27646+
"""""""
27647+
27648+
::
27649+
27650+
declare i1 @llvm.experimental.hot()
27651+
27652+
Overview:
27653+
"""""""""
27654+
27655+
This intrinsic returns true iff it's known that containing basic block is hot in
27656+
profile.
27657+
27658+
When used with profile based optimization allows to change program behaviour
27659+
deppending on the code hotness.
27660+
27661+
Arguments:
27662+
""""""""""
27663+
27664+
None.
27665+
27666+
Semantics:
27667+
""""""""""
27668+
27669+
The intrinsic ``@llvm.experimental.hot()`` returns either `true` or `false`,
27670+
deppending on profile used. Expresion is evaluated as `true` iff profile and
27671+
summary are availible and profile counter for the block reach hotness threshold.
27672+
For each evaluation of a call to this intrinsic, the program must be valid and
27673+
correct both if it returns `true` and if it returns `false`.
27674+
27675+
When used in a branch condition, it allows us to choose between
27676+
two alternative correct solutions for the same problem, like
27677+
in example below:
27678+
27679+
.. code-block:: text
27680+
27681+
%cond = call i1 @llvm.experimental.hot()
27682+
br i1 %cond, label %fast_path, label %slow_path
27683+
27684+
label %fast_path:
27685+
; Omit diagnostics.
27686+
27687+
label %slow_path:
27688+
; Additional diagnostics.
27689+
2764227690

2764327691
'``llvm.load.relative``' Intrinsic
2764427692
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1722,6 +1722,11 @@ def int_debugtrap : Intrinsic<[]>,
17221722
def int_ubsantrap : Intrinsic<[], [llvm_i8_ty],
17231723
[IntrNoReturn, IntrCold, ImmArg<ArgIndex<0>>]>;
17241724

1725+
// Return true if profile counter for containing block is hot.
1726+
def int_experimental_hot : Intrinsic<[llvm_i1_ty], [],
1727+
[IntrInaccessibleMemOnly, IntrWriteMem,
1728+
IntrWillReturn, NoUndef<RetIndex>]>;
1729+
17251730
// Support for dynamic deoptimization (or de-specialization)
17261731
def int_experimental_deoptimize : Intrinsic<[llvm_any_ty], [llvm_vararg_ty],
17271732
[Throws]>;

llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7276,6 +7276,12 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
72767276
setValue(&I, getValue(I.getArgOperand(0)));
72777277
return;
72787278

7279+
case Intrinsic::experimental_hot:
7280+
// Default lowering to false. It's intended to be lowered as soon as profile
7281+
// is avalible to unblock other optimizations.
7282+
setValue(&I, DAG.getConstant(0, sdl, MVT::i1));
7283+
return;
7284+
72797285
case Intrinsic::ubsantrap:
72807286
case Intrinsic::debugtrap:
72817287
case Intrinsic::trap: {

llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2827,6 +2827,14 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
28272827
}))
28282828
return nullptr;
28292829
break;
2830+
case Intrinsic::experimental_hot: {
2831+
// The intrinsic declaration includes sideeffects to avoid it moved. This
2832+
// prevents removing even if the intrinsic is unused. We should remove
2833+
// unused ones to enabled other optimizations.
2834+
if (CI.use_empty())
2835+
return eraseInstFromFunction(CI);
2836+
break;
2837+
}
28302838
case Intrinsic::assume: {
28312839
Value *IIOperand = II->getArgOperand(0);
28322840
SmallVector<OperandBundleDef, 4> OpBundles;

llvm/lib/Transforms/Instrumentation/RemoveTrapsPass.cpp

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "llvm/ADT/SmallVector.h"
1212
#include "llvm/ADT/Statistic.h"
1313
#include "llvm/Analysis/ProfileSummaryInfo.h"
14+
#include "llvm/IR/Constant.h"
1415
#include "llvm/IR/Instructions.h"
1516
#include "llvm/IR/IntrinsicInst.h"
1617
#include "llvm/IR/Intrinsics.h"
@@ -37,7 +38,7 @@ STATISTIC(NumChecksRemoved, "Number of removed checks");
3738

3839
static bool removeUbsanTraps(Function &F, const BlockFrequencyInfo &BFI,
3940
const ProfileSummaryInfo *PSI) {
40-
SmallVector<IntrinsicInst *, 16> Remove;
41+
SmallVector<std::pair<IntrinsicInst *, Value *>, 16> ReplaceWithValue;
4142
std::unique_ptr<RandomNumberGenerator> Rng;
4243

4344
auto ShouldRemove = [&](bool IsHot) {
@@ -56,26 +57,27 @@ static bool removeUbsanTraps(Function &F, const BlockFrequencyInfo &BFI,
5657
continue;
5758
auto ID = II->getIntrinsicID();
5859
switch (ID) {
59-
case Intrinsic::ubsantrap: {
60+
case Intrinsic::experimental_hot: {
6061
++NumChecksTotal;
6162

6263
bool IsHot = false;
6364
if (PSI) {
64-
uint64_t Count = 0;
65-
for (const auto *PR : predecessors(&BB))
66-
Count += BFI.getBlockProfileCount(PR).value_or(0);
67-
65+
uint64_t Count = BFI.getBlockProfileCount(&BB).value_or(0);
6866
IsHot =
6967
HotPercentileCutoff.getNumOccurrences()
7068
? (HotPercentileCutoff > 0 &&
7169
PSI->isHotCountNthPercentile(HotPercentileCutoff, Count))
7270
: PSI->isHotCount(Count);
7371
}
7472

75-
if (ShouldRemove(IsHot)) {
76-
Remove.push_back(II);
73+
bool ToRemove = ShouldRemove(IsHot);
74+
ReplaceWithValue.push_back({
75+
II,
76+
ToRemove ? Constant::getAllOnesValue(II->getType())
77+
: (Constant::getNullValue(II->getType())),
78+
});
79+
if (ToRemove)
7780
++NumChecksRemoved;
78-
}
7981
break;
8082
}
8183
default:
@@ -84,10 +86,12 @@ static bool removeUbsanTraps(Function &F, const BlockFrequencyInfo &BFI,
8486
}
8587
}
8688

87-
for (IntrinsicInst *I : Remove)
89+
for (auto [I, V] : ReplaceWithValue) {
90+
I->replaceAllUsesWith(V);
8891
I->eraseFromParent();
92+
}
8993

90-
return !Remove.empty();
94+
return !ReplaceWithValue.empty();
9195
}
9296

9397
PreservedAnalyses RemoveTrapsPass::run(Function &F,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 4
2+
; RUN: llc -o - %s | FileCheck %s
3+
4+
; REQUIRES: aarch64-registered-target
5+
6+
target triple = "aarch64-linux"
7+
8+
define i1 @test() {
9+
; CHECK-LABEL: test:
10+
; CHECK: // %bb.0: // %entry
11+
; CHECK-NEXT: mov w0, wzr
12+
; CHECK-NEXT: ret
13+
entry:
14+
%hot = call i1 @llvm.experimental.hot()
15+
ret i1 %hot
16+
}
17+
18+
declare i1 @llvm.expect.hot() nounwind
19+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
3+
4+
define i1 @test() {
5+
; CHECK-LABEL: @test(
6+
; CHECK-NEXT: entry:
7+
; CHECK-NEXT: [[HOT:%.*]] = call i1 @llvm.experimental.hot()
8+
; CHECK-NEXT: ret i1 [[HOT]]
9+
;
10+
entry:
11+
%hot = call i1 @llvm.experimental.hot()
12+
ret i1 %hot
13+
}
14+
15+
define void @test_void() {
16+
; CHECK-LABEL: @test_void(
17+
; CHECK-NEXT: entry:
18+
; CHECK-NEXT: ret void
19+
;
20+
entry:
21+
%hot = call i1 @llvm.experimental.hot()
22+
ret void
23+
}
24+
25+
declare i1 @llvm.expect.hot() nounwind

0 commit comments

Comments
 (0)