Skip to content

Commit a10802f

Browse files
committedSep 10, 2019
clang-misexpect: Profile Guided Validation of Performance Annotations in LLVM
This patch contains the basic functionality for reporting potentially incorrect usage of __builtin_expect() by comparing the developer's annotation against a collected PGO profile. A more detailed proposal and discussion appears on the CFE-dev mailing list (http://lists.llvm.org/pipermail/cfe-dev/2019-July/062971.html) and a prototype of the initial frontend changes appear here in D65300 We revised the work in D65300 by moving the misexpect check into the LLVM backend, and adding support for IR and sampling based profiles, in addition to frontend instrumentation. We add new misexpect metadata tags to those instructions directly influenced by the llvm.expect intrinsic (branch, switch, and select) when lowering the intrinsics. The misexpect metadata contains information about the expected target of the intrinsic so that we can check against the correct PGO counter when emitting diagnostics, and the compiler's values for the LikelyBranchWeight and UnlikelyBranchWeight. We use these branch weight values to determine when to emit the diagnostic to the user. A future patch should address the comment at the top of LowerExpectIntrisic.cpp to hoist the LikelyBranchWeight and UnlikelyBranchWeight values into a shared space that can be accessed outside of the LowerExpectIntrinsic pass. Once that is done, the misexpect metadata can be updated to be smaller. In the long term, it is possible to reconstruct portions of the misexpect metadata from the existing profile data. However, we have avoided this to keep the code simple, and because some kind of metadata tag will be required to identify which branch/switch/select instructions are influenced by the use of llvm.expect Patch By: paulkirth Differential Revision: https://reviews.llvm.org/D66324 llvm-svn: 371484
1 parent 73da43a commit a10802f

40 files changed

+1704
-26
lines changed
 

‎clang/include/clang/Basic/DiagnosticFrontendKinds.td

+6-1
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,12 @@ def warn_profile_data_missing : Warning<
275275
def warn_profile_data_unprofiled : Warning<
276276
"no profile data available for file \"%0\"">,
277277
InGroup<ProfileInstrUnprofiled>;
278-
278+
def warn_profile_data_misexpect : Warning<
279+
"Potential performance regression from use of __builtin_expect(): "
280+
"Annotation was correct on %0 of profiled executions.">,
281+
BackendInfo,
282+
InGroup<MisExpect>,
283+
DefaultIgnore;
279284
} // end of instrumentation issue category
280285

281286
}

‎clang/include/clang/Basic/DiagnosticGroups.td

+1
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,7 @@ def BackendOptimizationFailure : DiagGroup<"pass-failed">;
10421042
def ProfileInstrMissing : DiagGroup<"profile-instr-missing">;
10431043
def ProfileInstrOutOfDate : DiagGroup<"profile-instr-out-of-date">;
10441044
def ProfileInstrUnprofiled : DiagGroup<"profile-instr-unprofiled">;
1045+
def MisExpect : DiagGroup<"misexpect">;
10451046

10461047
// AddressSanitizer frontend instrumentation remarks.
10471048
def SanitizeAddressRemarks : DiagGroup<"sanitize-address">;

‎clang/lib/CodeGen/CodeGenAction.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "clang/AST/ASTContext.h"
1515
#include "clang/AST/DeclCXX.h"
1616
#include "clang/AST/DeclGroup.h"
17+
#include "clang/Basic/DiagnosticFrontend.h"
1718
#include "clang/Basic/FileManager.h"
1819
#include "clang/Basic/LangStandard.h"
1920
#include "clang/Basic/SourceManager.h"
@@ -365,6 +366,9 @@ namespace clang {
365366
bool StackSizeDiagHandler(const llvm::DiagnosticInfoStackSize &D);
366367
/// Specialized handler for unsupported backend feature diagnostic.
367368
void UnsupportedDiagHandler(const llvm::DiagnosticInfoUnsupported &D);
369+
/// Specialized handler for misexpect warnings.
370+
/// Note that misexpect remarks are emitted through ORE
371+
void MisExpectDiagHandler(const llvm::DiagnosticInfoMisExpect &D);
368372
/// Specialized handlers for optimization remarks.
369373
/// Note that these handlers only accept remarks and they always handle
370374
/// them.
@@ -617,6 +621,25 @@ void BackendConsumer::UnsupportedDiagHandler(
617621
<< Filename << Line << Column;
618622
}
619623

624+
void BackendConsumer::MisExpectDiagHandler(
625+
const llvm::DiagnosticInfoMisExpect &D) {
626+
StringRef Filename;
627+
unsigned Line, Column;
628+
bool BadDebugInfo = false;
629+
FullSourceLoc Loc =
630+
getBestLocationFromDebugLoc(D, BadDebugInfo, Filename, Line, Column);
631+
632+
Diags.Report(Loc, diag::warn_profile_data_misexpect) << D.getMsg().str();
633+
634+
if (BadDebugInfo)
635+
// If we were not able to translate the file:line:col information
636+
// back to a SourceLocation, at least emit a note stating that
637+
// we could not translate this location. This can happen in the
638+
// case of #line directives.
639+
Diags.Report(Loc, diag::note_fe_backend_invalid_loc)
640+
<< Filename << Line << Column;
641+
}
642+
620643
void BackendConsumer::EmitOptimizationMessage(
621644
const llvm::DiagnosticInfoOptimizationBase &D, unsigned DiagID) {
622645
// We only support warnings and remarks.
@@ -787,6 +810,9 @@ void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) {
787810
case llvm::DK_Unsupported:
788811
UnsupportedDiagHandler(cast<DiagnosticInfoUnsupported>(DI));
789812
return;
813+
case llvm::DK_MisExpect:
814+
MisExpectDiagHandler(cast<DiagnosticInfoMisExpect>(DI));
815+
return;
790816
default:
791817
// Plugin IDs are not bound to any value as they are set dynamically.
792818
ComputeDiagRemarkID(Severity, backend_plugin, DiagID);

‎clang/lib/Frontend/CompilerInvocation.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -3453,6 +3453,9 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res,
34533453
}
34543454
}
34553455

3456+
if (Diags.isIgnored(diag::warn_profile_data_misexpect, SourceLocation()))
3457+
Res.FrontendOpts.LLVMArgs.push_back("-pgo-warn-misexpect");
3458+
34563459
LangOpts.FunctionAlignment =
34573460
getLastArgIntValue(Args, OPT_function_alignment, 0, Diags);
34583461

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
bar
2+
# Func Hash:
3+
11262309464
4+
# Num Counters:
5+
2
6+
# Counter Values:
7+
200000
8+
2
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
bar
2+
# Func Hash:
3+
45795613684824
4+
# Num Counters:
5+
2
6+
# Counter Values:
7+
200000
8+
0
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
main
2+
# Func Hash:
3+
79676873694057560
4+
# Num Counters:
5+
5
6+
# Counter Values:
7+
1
8+
20
9+
20000
10+
20000
11+
20000
12+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
main
2+
# Func Hash:
3+
8712453512413296413
4+
# Num Counters:
5+
9
6+
# Counter Values:
7+
1
8+
20000
9+
20000
10+
4066
11+
11889
12+
0
13+
0
14+
4045
15+
0
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
main
2+
# Func Hash:
3+
1965403898329309329
4+
# Num Counters:
5+
9
6+
# Counter Values:
7+
1
8+
20
9+
20000
10+
20000
11+
12
12+
26
13+
0
14+
0
15+
19962
16+
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Test that misexpect emits no warning when prediction is correct
2+
3+
// RUN: llvm-profdata merge %S/Inputs/misexpect-branch.proftext -o %t.profdata
4+
// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -Wmisexpect
5+
6+
// expected-no-diagnostics
7+
#define likely(x) __builtin_expect(!!(x), 1)
8+
#define unlikely(x) __builtin_expect(!!(x), 0)
9+
10+
int foo(int);
11+
int baz(int);
12+
int buzz();
13+
14+
const int inner_loop = 100;
15+
const int outer_loop = 2000;
16+
17+
int bar() {
18+
int rando = buzz();
19+
int x = 0;
20+
if (unlikely(rando % (outer_loop * inner_loop) == 0)) {
21+
x = baz(rando);
22+
} else {
23+
x = foo(50);
24+
}
25+
return x;
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Test that misexpect emits no warning when condition is not a compile-time constant
2+
3+
// RUN: llvm-profdata merge %S/Inputs/misexpect-branch-nonconst-expect-arg.proftext -o %t.profdata
4+
// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -Wmisexpect
5+
6+
// expected-no-diagnostics
7+
int foo(int);
8+
int baz(int);
9+
int buzz();
10+
11+
const int inner_loop = 100;
12+
const int outer_loop = 2000;
13+
14+
int bar() {
15+
int rando = buzz();
16+
int x = 0;
17+
if (__builtin_expect(rando % (outer_loop * inner_loop) == 0, buzz())) {
18+
x = baz(rando);
19+
} else {
20+
x = foo(50);
21+
}
22+
return x;
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Test that misexpect emits no warning when prediction is correct
2+
3+
// RUN: llvm-profdata merge %S/Inputs/misexpect-branch.proftext -o %t.profdata
4+
// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -Wmisexpect
5+
6+
// expected-no-diagnostics
7+
#define unpredictable(x) __builtin_unpredictable(!!(x))
8+
9+
int foo(int);
10+
int baz(int);
11+
int buzz();
12+
13+
const int inner_loop = 100;
14+
const int outer_loop = 2000;
15+
16+
int bar() {
17+
int rando = buzz();
18+
int x = 0;
19+
if (unpredictable(rando % (outer_loop * inner_loop) == 0)) {
20+
x = baz(rando);
21+
} else {
22+
x = foo(50);
23+
}
24+
return x;
25+
}

‎clang/test/Profile/misexpect-branch.c

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Test that misexpect detects mis-annotated branches
2+
3+
// RUN: llvm-profdata merge %S/Inputs/misexpect-branch.proftext -o %t.profdata
4+
// RUN: %clang_cc1 %s -O2 -o - -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify=imprecise -Wmisexpect
5+
// RUN: %clang_cc1 %s -O2 -o - -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify=exact -Wmisexpect -debug-info-kind=line-tables-only
6+
// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify=foo
7+
8+
// foo-no-diagnostics
9+
#define likely(x) __builtin_expect(!!(x), 1)
10+
#define unlikely(x) __builtin_expect(!!(x), 0)
11+
12+
int foo(int);
13+
int baz(int);
14+
int buzz();
15+
16+
const int inner_loop = 100;
17+
const int outer_loop = 2000;
18+
19+
int bar() { // imprecise-warning-re {{Potential performance regression from use of __builtin_expect(): Annotation was correct on {{.+}}% ({{[0-9]+ / [0-9]+}}) of profiled executions.}}
20+
int rando = buzz();
21+
int x = 0;
22+
if (likely(rando % (outer_loop * inner_loop) == 0)) { // exact-warning-re {{Potential performance regression from use of __builtin_expect(): Annotation was correct on {{.+}}% ({{[0-9]+ / [0-9]+}}) of profiled executions.}}
23+
x = baz(rando);
24+
} else {
25+
x = foo(50);
26+
}
27+
return x;
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Test that misexpect detects mis-annotated switch statements for default case
2+
3+
// RUN: llvm-profdata merge %S/Inputs/misexpect-switch-default.proftext -o %t.profdata
4+
// RUN: %clang_cc1 %s -O2 -o - -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -Wmisexpect -debug-info-kind=line-tables-only
5+
6+
int sum(int *buff, int size);
7+
int random_sample(int *buff, int size);
8+
int rand();
9+
void init_arry();
10+
11+
const int inner_loop = 1000;
12+
const int outer_loop = 20;
13+
const int arry_size = 25;
14+
15+
int arry[arry_size] = {0};
16+
17+
int main() {
18+
init_arry();
19+
int val = 0;
20+
int j;
21+
for (j = 0; j < outer_loop * inner_loop; ++j) {
22+
unsigned condition = rand() % 5;
23+
switch (__builtin_expect(condition, 6)) { // expected-warning-re {{Potential performance regression from use of __builtin_expect(): Annotation was correct on {{.+}}% ({{[0-9]+ / [0-9]+}}) of profiled executions.}}
24+
case 0:
25+
val += sum(arry, arry_size);
26+
break;
27+
case 1:
28+
case 2:
29+
case 3:
30+
break;
31+
case 4:
32+
val += random_sample(arry, arry_size);
33+
break;
34+
default:
35+
__builtin_unreachable();
36+
} // end switch
37+
} // end outer_loop
38+
39+
return 0;
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Test that misexpect emits no warning when switch condition is non-const
2+
3+
// RUN: llvm-profdata merge %S/Inputs/misexpect-switch.proftext -o %t.profdata
4+
// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -Wmisexpect
5+
6+
// expected-no-diagnostics
7+
int sum(int *buff, int size);
8+
int random_sample(int *buff, int size);
9+
int rand();
10+
void init_arry();
11+
12+
const int inner_loop = 1000;
13+
const int outer_loop = 20;
14+
const int arry_size = 25;
15+
16+
int arry[arry_size] = {0};
17+
18+
int main() {
19+
init_arry();
20+
int val = 0;
21+
22+
int j, k;
23+
for (j = 0; j < outer_loop; ++j) {
24+
for (k = 0; k < inner_loop; ++k) {
25+
unsigned condition = rand() % 10000;
26+
switch (__builtin_expect(condition, rand())) {
27+
case 0:
28+
val += sum(arry, arry_size);
29+
break;
30+
case 1:
31+
case 2:
32+
case 3:
33+
case 4:
34+
val += random_sample(arry, arry_size);
35+
break;
36+
default:
37+
__builtin_unreachable();
38+
} // end switch
39+
} // end inner_loop
40+
} // end outer_loop
41+
42+
return 0;
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Test that misexpect emits no warning when there is only one switch case
2+
3+
// RUN: llvm-profdata merge %S/Inputs/misexpect-switch-default-only.proftext -o %t.profdata
4+
// RUN: %clang_cc1 %s -O2 -o - -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -Wmisexpect -debug-info-kind=line-tables-only
5+
6+
// expected-no-diagnostics
7+
int sum(int *buff, int size);
8+
int random_sample(int *buff, int size);
9+
int rand();
10+
void init_arry();
11+
12+
const int inner_loop = 1000;
13+
const int outer_loop = 20;
14+
const int arry_size = 25;
15+
16+
int arry[arry_size] = {0};
17+
18+
int main() {
19+
init_arry();
20+
int val = 0;
21+
22+
int j, k;
23+
for (j = 0; j < outer_loop; ++j) {
24+
for (k = 0; k < inner_loop; ++k) {
25+
unsigned condition = rand() % 10000;
26+
switch (__builtin_expect(condition, 0)) {
27+
default:
28+
val += random_sample(arry, arry_size);
29+
break;
30+
}; // end switch
31+
} // end inner_loop
32+
} // end outer_loop
33+
34+
return 0;
35+
}

‎clang/test/Profile/misexpect-switch.c

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Test that misexpect detects mis-annotated switch statements
2+
3+
// RUN: llvm-profdata merge %S/Inputs/misexpect-switch.proftext -o %t.profdata
4+
// RUN: %clang_cc1 %s -O2 -o - -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -Wmisexpect -debug-info-kind=line-tables-only
5+
6+
int sum(int *buff, int size);
7+
int random_sample(int *buff, int size);
8+
int rand();
9+
void init_arry();
10+
11+
const int inner_loop = 1000;
12+
const int outer_loop = 20;
13+
const int arry_size = 25;
14+
15+
int arry[arry_size] = {0};
16+
17+
int main() {
18+
init_arry();
19+
int val = 0;
20+
21+
int j, k;
22+
for (j = 0; j < outer_loop; ++j) {
23+
for (k = 0; k < inner_loop; ++k) {
24+
unsigned condition = rand() % 10000;
25+
switch (__builtin_expect(condition, 0)) { // expected-warning-re {{Potential performance regression from use of __builtin_expect(): Annotation was correct on {{.+}}% ({{[0-9]+ / [0-9]+}}) of profiled executions.}}
26+
case 0:
27+
val += sum(arry, arry_size);
28+
break;
29+
case 1:
30+
case 2:
31+
case 3:
32+
break;
33+
default:
34+
val += random_sample(arry, arry_size);
35+
break;
36+
} // end switch
37+
} // end inner_loop
38+
} // end outer_loop
39+
40+
return 0;
41+
}

‎llvm/include/llvm/IR/DiagnosticInfo.h

+21-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ enum DiagnosticKind {
7575
DK_MIRParser,
7676
DK_PGOProfile,
7777
DK_Unsupported,
78-
DK_FirstPluginKind
78+
DK_FirstPluginKind,
79+
DK_MisExpect
7980
};
8081

8182
/// Get the next available kind ID for a plugin diagnostic.
@@ -1002,6 +1003,25 @@ class DiagnosticInfoUnsupported : public DiagnosticInfoWithLocationBase {
10021003
void print(DiagnosticPrinter &DP) const override;
10031004
};
10041005

1006+
/// Diagnostic information for MisExpect analysis.
1007+
class DiagnosticInfoMisExpect : public DiagnosticInfoWithLocationBase {
1008+
public:
1009+
DiagnosticInfoMisExpect(const Instruction *Inst, Twine &Msg);
1010+
1011+
/// \see DiagnosticInfo::print.
1012+
void print(DiagnosticPrinter &DP) const override;
1013+
1014+
static bool classof(const DiagnosticInfo *DI) {
1015+
return DI->getKind() == DK_MisExpect;
1016+
}
1017+
1018+
const Twine &getMsg() const { return Msg; }
1019+
1020+
private:
1021+
/// Message to report.
1022+
const Twine &Msg;
1023+
};
1024+
10051025
} // end namespace llvm
10061026

10071027
#endif // LLVM_IR_DIAGNOSTICINFO_H

‎llvm/include/llvm/IR/FixedMetadataKinds.def

+1
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ LLVM_FIXED_MD_KIND(MD_irr_loop, "irr_loop", 24)
3939
LLVM_FIXED_MD_KIND(MD_access_group, "llvm.access.group", 25)
4040
LLVM_FIXED_MD_KIND(MD_callback, "callback", 26)
4141
LLVM_FIXED_MD_KIND(MD_preserve_access_index, "llvm.preserve.access.index", 27)
42+
LLVM_FIXED_MD_KIND(MD_misexpect, "misexpect", 28)

‎llvm/include/llvm/IR/MDBuilder.h

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "llvm/ADT/DenseSet.h"
1818
#include "llvm/ADT/StringRef.h"
19+
#include "llvm/IR/Constants.h"
1920
#include "llvm/IR/GlobalValue.h"
2021
#include "llvm/Support/DataTypes.h"
2122
#include <utility>
@@ -75,6 +76,10 @@ class MDBuilder {
7576
/// Return metadata containing the section prefix for a function.
7677
MDNode *createFunctionSectionPrefix(StringRef Prefix);
7778

79+
/// return metadata containing expected value
80+
MDNode *createMisExpect(uint64_t Index, uint64_t LikelyWeight,
81+
uint64_t UnlikelyWeight);
82+
7883
//===------------------------------------------------------------------===//
7984
// Range metadata.
8085
//===------------------------------------------------------------------===//
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//===--- MisExpect.h - Check the use of llvm.expect with PGO data ---------===//
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+
// This contains code to emit warnings for potentially incorrect usage of the
10+
// llvm.expect intrinsic. This utility extracts the threshold values from
11+
// metadata associated with the instrumented Branch or Switch instruction. The
12+
// threshold values are then used to determine if a warning should be emmited.
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
#include "llvm/ADT/SmallVector.h"
17+
#include "llvm/IR/Function.h"
18+
#include "llvm/IR/Instructions.h"
19+
#include "llvm/IR/LLVMContext.h"
20+
21+
namespace llvm {
22+
namespace misexpect {
23+
24+
/// verifyMisExpect - compares PGO counters to the thresholds used for
25+
/// llvm.expect and warns if the PGO counters are outside of the expected
26+
/// range.
27+
/// \param I The Instruction being checked
28+
/// \param Weights A vector of profile weights for each target block
29+
/// \param Ctx The current LLVM context
30+
void verifyMisExpect(llvm::Instruction *I,
31+
const llvm::SmallVector<uint32_t, 4> &Weights,
32+
llvm::LLVMContext &Ctx);
33+
34+
/// checkClangInstrumentation - verify if llvm.expect matches PGO profile
35+
/// This function checks the frontend instrumentation in the backend when
36+
/// lowering llvm.expect intrinsics. It checks for existing metadata, and
37+
/// then validates the use of llvm.expect against the assigned branch weights.
38+
//
39+
/// \param I the Instruction being checked
40+
void checkFrontendInstrumentation(Instruction &I);
41+
42+
} // namespace misexpect
43+
} // namespace llvm

‎llvm/lib/IR/DiagnosticInfo.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -370,5 +370,16 @@ std::string DiagnosticInfoOptimizationBase::getMsg() const {
370370
return OS.str();
371371
}
372372

373+
DiagnosticInfoMisExpect::DiagnosticInfoMisExpect(const Instruction *Inst,
374+
Twine &Msg)
375+
: DiagnosticInfoWithLocationBase(DK_MisExpect, DS_Warning,
376+
*Inst->getParent()->getParent(),
377+
Inst->getDebugLoc()),
378+
Msg(Msg) {}
379+
380+
void DiagnosticInfoMisExpect::print(DiagnosticPrinter &DP) const {
381+
DP << getLocationStr() << ": " << getMsg();
382+
}
383+
373384
void OptimizationRemarkAnalysisFPCommute::anchor() {}
374385
void OptimizationRemarkAnalysisAliasing::anchor() {}

‎llvm/lib/IR/MDBuilder.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -309,3 +309,15 @@ MDNode *MDBuilder::createIrrLoopHeaderWeight(uint64_t Weight) {
309309
};
310310
return MDNode::get(Context, Vals);
311311
}
312+
313+
MDNode *MDBuilder::createMisExpect(uint64_t Index, uint64_t LikleyWeight,
314+
uint64_t UnlikleyWeight) {
315+
auto *IntType = Type::getInt64Ty(Context);
316+
Metadata *Vals[] = {
317+
createString("misexpect"),
318+
createConstant(ConstantInt::get(IntType, Index)),
319+
createConstant(ConstantInt::get(IntType, LikleyWeight)),
320+
createConstant(ConstantInt::get(IntType, UnlikleyWeight)),
321+
};
322+
return MDNode::get(Context, Vals);
323+
}

‎llvm/lib/Transforms/IPO/SampleProfile.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
#include "llvm/Transforms/Instrumentation.h"
7373
#include "llvm/Transforms/Utils/CallPromotionUtils.h"
7474
#include "llvm/Transforms/Utils/Cloning.h"
75+
#include "llvm/Transforms/Utils/MisExpect.h"
7576
#include <algorithm>
7677
#include <cassert>
7778
#include <cstdint>
@@ -1446,6 +1447,8 @@ void SampleProfileLoader::propagateWeights(Function &F) {
14461447
}
14471448
}
14481449

1450+
misexpect::verifyMisExpect(TI, Weights, TI->getContext());
1451+
14491452
uint64_t TempWeight;
14501453
// Only set weights if there is at least one non-zero weight.
14511454
// In any other case, let the analyzer set weights.

‎llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
#include "llvm/Transforms/Instrumentation.h"
109109
#include "llvm/Transforms/Instrumentation/PGOInstrumentation.h"
110110
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
111+
#include "llvm/Transforms/Utils/MisExpect.h"
111112
#include <algorithm>
112113
#include <cassert>
113114
#include <cstdint>
@@ -1776,6 +1777,9 @@ void llvm::setProfMetadata(Module *M, Instruction *TI,
17761777
: Weights) {
17771778
dbgs() << W << " ";
17781779
} dbgs() << "\n";);
1780+
1781+
misexpect::verifyMisExpect(TI, Weights, TI->getContext());
1782+
17791783
TI->setMetadata(LLVMContext::MD_prof, MDB.createBranchWeights(Weights));
17801784
if (EmitBranchProbability) {
17811785
std::string BrCondStr = getBranchCondString(TI);

‎llvm/lib/Transforms/Scalar/LowerExpectIntrinsic.cpp

+23-8
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/Support/CommandLine.h"
2727
#include "llvm/Support/Debug.h"
2828
#include "llvm/Transforms/Scalar.h"
29+
#include "llvm/Transforms/Utils/MisExpect.h"
2930

3031
using namespace llvm;
3132

@@ -71,15 +72,20 @@ static bool handleSwitchExpect(SwitchInst &SI) {
7172
unsigned n = SI.getNumCases(); // +1 for default case.
7273
SmallVector<uint32_t, 16> Weights(n + 1, UnlikelyBranchWeight);
7374

74-
if (Case == *SI.case_default())
75-
Weights[0] = LikelyBranchWeight;
76-
else
77-
Weights[Case.getCaseIndex() + 1] = LikelyBranchWeight;
75+
uint64_t Index = (Case == *SI.case_default()) ? 0 : Case.getCaseIndex() + 1;
76+
Weights[Index] = LikelyBranchWeight;
77+
78+
SI.setMetadata(
79+
LLVMContext::MD_misexpect,
80+
MDBuilder(CI->getContext())
81+
.createMisExpect(Index, LikelyBranchWeight, UnlikelyBranchWeight));
82+
83+
SI.setCondition(ArgValue);
84+
misexpect::checkFrontendInstrumentation(SI);
7885

7986
SI.setMetadata(LLVMContext::MD_prof,
8087
MDBuilder(CI->getContext()).createBranchWeights(Weights));
8188

82-
SI.setCondition(ArgValue);
8389
return true;
8490
}
8591

@@ -280,19 +286,28 @@ template <class BrSelInst> static bool handleBrSelExpect(BrSelInst &BSI) {
280286

281287
MDBuilder MDB(CI->getContext());
282288
MDNode *Node;
289+
MDNode *ExpNode;
283290

284291
if ((ExpectedValue->getZExtValue() == ValueComparedTo) ==
285-
(Predicate == CmpInst::ICMP_EQ))
292+
(Predicate == CmpInst::ICMP_EQ)) {
286293
Node = MDB.createBranchWeights(LikelyBranchWeight, UnlikelyBranchWeight);
287-
else
294+
ExpNode = MDB.createMisExpect(0, LikelyBranchWeight, UnlikelyBranchWeight);
295+
} else {
288296
Node = MDB.createBranchWeights(UnlikelyBranchWeight, LikelyBranchWeight);
297+
ExpNode = MDB.createMisExpect(1, LikelyBranchWeight, UnlikelyBranchWeight);
298+
}
289299

290-
BSI.setMetadata(LLVMContext::MD_prof, Node);
300+
BSI.setMetadata(LLVMContext::MD_misexpect, ExpNode);
291301

292302
if (CmpI)
293303
CmpI->setOperand(0, ArgValue);
294304
else
295305
BSI.setCondition(ArgValue);
306+
307+
misexpect::checkFrontendInstrumentation(BSI);
308+
309+
BSI.setMetadata(LLVMContext::MD_prof, Node);
310+
296311
return true;
297312
}
298313

‎llvm/lib/Transforms/Utils/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ add_llvm_library(LLVMTransformUtils
4040
LowerSwitch.cpp
4141
Mem2Reg.cpp
4242
MetaRenamer.cpp
43+
MisExpect.cpp
4344
ModuleUtils.cpp
4445
NameAnonGlobals.cpp
4546
PredicateInfo.cpp
+177
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
//===--- MisExpect.cpp - Check the use of llvm.expect with PGO data -------===//
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+
// This contains code to emit warnings for potentially incorrect usage of the
10+
// llvm.expect intrinsic. This utility extracts the threshold values from
11+
// metadata associated with the instrumented Branch or Switch instruction. The
12+
// threshold values are then used to determine if a warning should be emmited.
13+
//
14+
// MisExpect metadata is generated when llvm.expect intrinsics are lowered see
15+
// LowerExpectIntrinsic.cpp
16+
//
17+
//===----------------------------------------------------------------------===//
18+
19+
#include "llvm/Transforms/Utils/MisExpect.h"
20+
#include "llvm/ADT/Twine.h"
21+
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
22+
#include "llvm/IR/Constants.h"
23+
#include "llvm/IR/DiagnosticInfo.h"
24+
#include "llvm/IR/Instruction.h"
25+
#include "llvm/IR/Instructions.h"
26+
#include "llvm/IR/LLVMContext.h"
27+
#include "llvm/Support/BranchProbability.h"
28+
#include "llvm/Support/Debug.h"
29+
#include "llvm/Support/FormatVariadic.h"
30+
#include <cstdint>
31+
#include <functional>
32+
#include <numeric>
33+
34+
#define DEBUG_TYPE "misexpect"
35+
36+
using namespace llvm;
37+
using namespace misexpect;
38+
39+
namespace llvm {
40+
41+
// Command line option to enable/disable the warning when profile data suggests
42+
// a mismatch with the use of the llvm.expect intrinsic
43+
static cl::opt<bool> PGOWarnMisExpect(
44+
"pgo-warn-misexpect", cl::init(false), cl::Hidden,
45+
cl::desc("Use this option to turn on/off "
46+
"warnings about incorrect usage of llvm.expect intrinsics."));
47+
48+
} // namespace llvm
49+
50+
namespace {
51+
52+
Instruction *getOprndOrInst(Instruction *I) {
53+
assert(I != nullptr && "MisExpect target Instruction cannot be nullptr");
54+
Instruction *Ret = nullptr;
55+
if (auto *B = dyn_cast<BranchInst>(I)) {
56+
Ret = dyn_cast<Instruction>(B->getCondition());
57+
}
58+
// TODO: Find a way to resolve condition location for switches
59+
// Using the condition of the switch seems to often resolve to an earlier
60+
// point in the program, i.e. the calculation of the switch condition, rather
61+
// than the switches location in the source code. Thus, we should use the
62+
// instruction to get source code locations rather than the condition to
63+
// improve diagnostic output, such as the caret. If the same problem exists
64+
// for branch instructions, then we should remove this function and directly
65+
// use the instruction
66+
//
67+
// else if (auto S = dyn_cast<SwitchInst>(I)) {
68+
// Ret = I;
69+
//}
70+
return Ret ? Ret : I;
71+
}
72+
73+
void emitMisexpectDiagnostic(Instruction *I, LLVMContext &Ctx,
74+
uint64_t ProfCount, uint64_t TotalCount) {
75+
double PercentageCorrect = (double)ProfCount / TotalCount;
76+
auto PerString =
77+
formatv("{0:P} ({1} / {2})", PercentageCorrect, ProfCount, TotalCount);
78+
auto RemStr = formatv(
79+
"Potential performance regression from use of the llvm.expect intrinsic: "
80+
"Annotation was correct on {0} of profiled executions.",
81+
PerString);
82+
Twine Msg(PerString);
83+
Instruction *Cond = getOprndOrInst(I);
84+
if (PGOWarnMisExpect)
85+
Ctx.diagnose(DiagnosticInfoMisExpect(Cond, Msg));
86+
OptimizationRemarkEmitter ORE(I->getParent()->getParent());
87+
ORE.emit(OptimizationRemark(DEBUG_TYPE, "misexpect", Cond) << RemStr.str());
88+
}
89+
90+
} // namespace
91+
92+
namespace llvm {
93+
namespace misexpect {
94+
95+
void verifyMisExpect(Instruction *I, const SmallVector<uint32_t, 4> &Weights,
96+
LLVMContext &Ctx) {
97+
if (auto *MisExpectData = I->getMetadata(LLVMContext::MD_misexpect)) {
98+
auto *MisExpectDataName = dyn_cast<MDString>(MisExpectData->getOperand(0));
99+
if (MisExpectDataName &&
100+
MisExpectDataName->getString().equals("misexpect")) {
101+
LLVM_DEBUG(llvm::dbgs() << "------------------\n");
102+
LLVM_DEBUG(llvm::dbgs()
103+
<< "Function: " << I->getFunction()->getName() << "\n");
104+
LLVM_DEBUG(llvm::dbgs() << "Instruction: " << *I << ":\n");
105+
LLVM_DEBUG(for (int Idx = 0, Size = Weights.size(); Idx < Size; ++Idx) {
106+
llvm::dbgs() << "Weights[" << Idx << "] = " << Weights[Idx] << "\n";
107+
});
108+
109+
// extract values from misexpect metadata
110+
const auto *IndexCint =
111+
mdconst::dyn_extract<ConstantInt>(MisExpectData->getOperand(1));
112+
const auto *LikelyCInt =
113+
mdconst::dyn_extract<ConstantInt>(MisExpectData->getOperand(2));
114+
const auto *UnlikelyCInt =
115+
mdconst::dyn_extract<ConstantInt>(MisExpectData->getOperand(3));
116+
117+
if (!IndexCint || !LikelyCInt || !UnlikelyCInt)
118+
return;
119+
120+
const uint64_t Index = IndexCint->getZExtValue();
121+
const uint64_t LikelyBranchWeight = LikelyCInt->getZExtValue();
122+
const uint64_t UnlikelyBranchWeight = UnlikelyCInt->getZExtValue();
123+
const uint64_t ProfileCount = Weights[Index];
124+
const uint64_t CaseTotal = std::accumulate(
125+
Weights.begin(), Weights.end(), (uint64_t)0, std::plus<uint64_t>());
126+
const uint64_t NumUnlikelyTargets = Weights.size() - 1;
127+
128+
const uint64_t TotalBranchWeight =
129+
LikelyBranchWeight + (UnlikelyBranchWeight * NumUnlikelyTargets);
130+
131+
const llvm::BranchProbability LikelyThreshold(LikelyBranchWeight,
132+
TotalBranchWeight);
133+
uint64_t ScaledThreshold = LikelyThreshold.scale(CaseTotal);
134+
135+
LLVM_DEBUG(llvm::dbgs()
136+
<< "Unlikely Targets: " << NumUnlikelyTargets << ":\n");
137+
LLVM_DEBUG(llvm::dbgs() << "Profile Count: " << ProfileCount << ":\n");
138+
LLVM_DEBUG(llvm::dbgs()
139+
<< "Scaled Threshold: " << ScaledThreshold << ":\n");
140+
LLVM_DEBUG(llvm::dbgs() << "------------------\n");
141+
if (ProfileCount < ScaledThreshold)
142+
emitMisexpectDiagnostic(I, Ctx, ProfileCount, CaseTotal);
143+
}
144+
}
145+
}
146+
147+
void checkFrontendInstrumentation(Instruction &I) {
148+
if (auto *MD = I.getMetadata(LLVMContext::MD_prof)) {
149+
unsigned NOps = MD->getNumOperands();
150+
151+
// Only emit misexpect diagnostics if at least 2 branch weights are present.
152+
// Less than 2 branch weights means that the profiling metadata is:
153+
// 1) incorrect/corrupted
154+
// 2) not branch weight metadata
155+
// 3) completely deterministic
156+
// In these cases we should not emit any diagnostic related to misexpect.
157+
if (NOps < 3)
158+
return;
159+
160+
// Operand 0 is a string tag "branch_weights"
161+
if (MDString *Tag = cast<MDString>(MD->getOperand(0))) {
162+
if (Tag->getString().equals("branch_weights")) {
163+
SmallVector<uint32_t, 4> RealWeights(NOps - 1);
164+
for (unsigned i = 1; i < NOps; i++) {
165+
ConstantInt *Value =
166+
mdconst::dyn_extract<ConstantInt>(MD->getOperand(i));
167+
RealWeights[i - 1] = Value->getZExtValue();
168+
}
169+
verifyMisExpect(&I, RealWeights, I.getContext());
170+
}
171+
}
172+
}
173+
}
174+
175+
} // namespace misexpect
176+
} // namespace llvm
177+
#undef DEBUG_TYPE

‎llvm/test/ThinLTO/X86/lazyload_metadata.ll

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010
; RUN: llvm-lto -thinlto-action=import %t2.bc -thinlto-index=%t3.bc \
1111
; RUN: -o /dev/null -stats \
1212
; RUN: 2>&1 | FileCheck %s -check-prefix=LAZY
13-
; LAZY: 61 bitcode-reader - Number of Metadata records loaded
13+
; LAZY: 63 bitcode-reader - Number of Metadata records loaded
1414
; LAZY: 2 bitcode-reader - Number of MDStrings loaded
1515

1616
; RUN: llvm-lto -thinlto-action=import %t2.bc -thinlto-index=%t3.bc \
1717
; RUN: -o /dev/null -disable-ondemand-mds-loading -stats \
1818
; RUN: 2>&1 | FileCheck %s -check-prefix=NOTLAZY
19-
; NOTLAZY: 70 bitcode-reader - Number of Metadata records loaded
19+
; NOTLAZY: 72 bitcode-reader - Number of Metadata records loaded
2020
; NOTLAZY: 7 bitcode-reader - Number of MDStrings loaded
2121

2222

‎llvm/test/Transforms/LowerExpectIntrinsic/basic.ll

+17-14
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ entry:
1313
%conv1 = sext i32 %conv to i64
1414
%expval = call i64 @llvm.expect.i64(i64 %conv1, i64 1)
1515
%tobool = icmp ne i64 %expval, 0
16-
; CHECK: !prof !0
16+
; CHECK: !prof !0, !misexpect !1
1717
; CHECK-NOT: @llvm.expect
1818
br i1 %tobool, label %if.then, label %if.end
1919

@@ -45,7 +45,7 @@ entry:
4545
%conv = sext i32 %tmp to i64
4646
%expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
4747
%tobool = icmp ne i64 %expval, 0
48-
; CHECK: !prof !0
48+
; CHECK: !prof !0, !misexpect !1
4949
; CHECK-NOT: @llvm.expect
5050
br i1 %tobool, label %if.then, label %if.end
5151

@@ -76,7 +76,7 @@ entry:
7676
%conv = sext i32 %lnot.ext to i64
7777
%expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
7878
%tobool1 = icmp ne i64 %expval, 0
79-
; CHECK: !prof !0
79+
; CHECK: !prof !0, !misexpect !1
8080
; CHECK-NOT: @llvm.expect
8181
br i1 %tobool1, label %if.then, label %if.end
8282

@@ -108,7 +108,7 @@ entry:
108108
%conv = sext i32 %lnot.ext to i64
109109
%expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
110110
%tobool2 = icmp ne i64 %expval, 0
111-
; CHECK: !prof !0
111+
; CHECK: !prof !0, !misexpect !1
112112
; CHECK-NOT: @llvm.expect
113113
br i1 %tobool2, label %if.then, label %if.end
114114

@@ -138,7 +138,7 @@ entry:
138138
%conv1 = sext i32 %conv to i64
139139
%expval = call i64 @llvm.expect.i64(i64 %conv1, i64 0)
140140
%tobool = icmp ne i64 %expval, 0
141-
; CHECK: !prof !1
141+
; CHECK: !prof !2, !misexpect !3
142142
; CHECK-NOT: @llvm.expect
143143
br i1 %tobool, label %if.then, label %if.end
144144

@@ -164,8 +164,8 @@ entry:
164164
store i32 %x, i32* %x.addr, align 4
165165
%tmp = load i32, i32* %x.addr, align 4
166166
%conv = sext i32 %tmp to i64
167-
%expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
168-
; CHECK: !prof !2
167+
%expval = call i64 @llvm.expect.i64(i64 %conv, i64 2)
168+
; CHECK: !prof !4, !misexpect !5
169169
; CHECK-NOT: @llvm.expect
170170
switch i64 %expval, label %sw.epilog [
171171
i64 1, label %sw.bb
@@ -194,7 +194,7 @@ entry:
194194
%tmp = load i32, i32* %x.addr, align 4
195195
%conv = sext i32 %tmp to i64
196196
%expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
197-
; CHECK: !prof !3
197+
; CHECK: !prof !6, !misexpect !1
198198
; CHECK-NOT: @llvm.expect
199199
switch i64 %expval, label %sw.epilog [
200200
i64 2, label %sw.bb
@@ -226,7 +226,7 @@ entry:
226226
%conv = zext i1 %cmp to i32
227227
%expval = call i32 @llvm.expect.i32(i32 %conv, i32 1)
228228
%tobool = icmp ne i32 %expval, 0
229-
; CHECK: !prof !0
229+
; CHECK: !prof !0, !misexpect !1
230230
; CHECK-NOT: @llvm.expect
231231
br i1 %tobool, label %if.then, label %if.end
232232

@@ -255,7 +255,7 @@ entry:
255255
%tmp = load i32, i32* %x.addr, align 4
256256
%cmp = icmp sgt i32 %tmp, 1
257257
%expval = call i1 @llvm.expect.i1(i1 %cmp, i1 1)
258-
; CHECK: !prof !0
258+
; CHECK: !prof !0, !misexpect !1
259259
; CHECK-NOT: @llvm.expect
260260
br i1 %expval, label %if.then, label %if.end
261261

@@ -278,14 +278,17 @@ define i32 @test10(i64 %t6) {
278278
%t7 = call i64 @llvm.expect.i64(i64 %t6, i64 0)
279279
%t8 = icmp ne i64 %t7, 0
280280
%t9 = select i1 %t8, i32 1, i32 2
281-
; CHECK: select{{.*}}, !prof !1
281+
; CHECK: select{{.*}}, !prof !2, !misexpect !3
282282
ret i32 %t9
283283
}
284284

285285

286286
declare i1 @llvm.expect.i1(i1, i1) nounwind readnone
287287

288288
; CHECK: !0 = !{!"branch_weights", i32 2000, i32 1}
289-
; CHECK: !1 = !{!"branch_weights", i32 1, i32 2000}
290-
; CHECK: !2 = !{!"branch_weights", i32 1, i32 2000, i32 1}
291-
; CHECK: !3 = !{!"branch_weights", i32 2000, i32 1, i32 1}
289+
; CHECK: !1 = !{!"misexpect", i64 0, i64 2000, i64 1}
290+
; CHECK: !2 = !{!"branch_weights", i32 1, i32 2000}
291+
; CHECK: !3 = !{!"misexpect", i64 1, i64 2000, i64 1}
292+
; CHECK: !4 = !{!"branch_weights", i32 1, i32 1, i32 2000}
293+
; CHECK: !5 = !{!"misexpect", i64 2, i64 2000, i64 1}
294+
; CHECK: !6 = !{!"branch_weights", i32 2000, i32 1, i32 1}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# IR level Instrumentation Flag
2+
:ir
3+
bar
4+
# Func Hash:
5+
29667547796
6+
# Num Counters:
7+
2
8+
# Counter Values:
9+
200000
10+
0
11+
12+
baz
13+
# Func Hash:
14+
12884901887
15+
# Num Counters:
16+
1
17+
# Counter Values:
18+
399668
19+
20+
foo
21+
# Func Hash:
22+
29212902728
23+
# Num Counters:
24+
2
25+
# Counter Values:
26+
40803991
27+
1600332
28+
29+
main
30+
# Func Hash:
31+
41605652536
32+
# Num Counters:
33+
3
34+
# Counter Values:
35+
2000000
36+
2000
37+
1
38+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# IR level Instrumentation Flag
2+
:ir
3+
bar
4+
# Func Hash:
5+
29667547796
6+
# Num Counters:
7+
2
8+
# Counter Values:
9+
399668
10+
1600332
11+
12+
baz
13+
# Func Hash:
14+
12884901887
15+
# Num Counters:
16+
1
17+
# Counter Values:
18+
399668
19+
20+
foo
21+
# Func Hash:
22+
29212902728
23+
# Num Counters:
24+
2
25+
# Counter Values:
26+
40803991
27+
1600332
28+
29+
main
30+
# Func Hash:
31+
41605652536
32+
# Num Counters:
33+
3
34+
# Counter Values:
35+
2000000
36+
2000
37+
1
38+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# IR level Instrumentation Flag
2+
:ir
3+
main
4+
# Func Hash:
5+
74054140268
6+
# Num Counters:
7+
7
8+
# Counter Values:
9+
0
10+
0
11+
20000
12+
0
13+
0
14+
1
15+
0
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# IR level Instrumentation Flag
2+
:ir
3+
main
4+
# Func Hash:
5+
74054140268
6+
# Num Counters:
7+
7
8+
# Counter Values:
9+
3973
10+
3970
11+
0
12+
11889
13+
8111
14+
1
15+
0
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
; RUN: llvm-profdata merge %S/Inputs/misexpect-branch-correct.proftext -o %t.profdata
2+
3+
; RUN: opt < %s -lower-expect -pgo-instr-use -pgo-test-profile-file=%t.profdata -S -pgo-warn-misexpect -pass-remarks=misexpect 2>&1 | FileCheck %s
4+
5+
; New PM
6+
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -pgo-warn-misexpect -pass-remarks=misexpect -S 2>&1 | FileCheck %s
7+
8+
; CHECK-NOT: warning: {{.*}}
9+
; CHECK-NOT: remark: {{.*}}
10+
; CHECK: !{!"misexpect", i64 1, i64 2000, i64 1}
11+
12+
13+
; ModuleID = 'misexpect-branch-correct.c'
14+
source_filename = "misexpect-branch-correct.c"
15+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
16+
target triple = "x86_64-unknown-linux-gnu"
17+
18+
@inner_loop = constant i32 100, align 4
19+
@outer_loop = constant i32 2000, align 4
20+
21+
; Function Attrs: nounwind
22+
define i32 @bar() #0 {
23+
entry:
24+
%rando = alloca i32, align 4
25+
%x = alloca i32, align 4
26+
%0 = bitcast i32* %rando to i8*
27+
call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #4
28+
%call = call i32 (...) @buzz()
29+
store i32 %call, i32* %rando, align 4, !tbaa !3
30+
%1 = bitcast i32* %x to i8*
31+
call void @llvm.lifetime.start.p0i8(i64 4, i8* %1) #4
32+
store i32 0, i32* %x, align 4, !tbaa !3
33+
%2 = load i32, i32* %rando, align 4, !tbaa !3
34+
%rem = srem i32 %2, 200000
35+
%cmp = icmp eq i32 %rem, 0
36+
%lnot = xor i1 %cmp, true
37+
%lnot1 = xor i1 %lnot, true
38+
%lnot.ext = zext i1 %lnot1 to i32
39+
%conv = sext i32 %lnot.ext to i64
40+
%expval = call i64 @llvm.expect.i64(i64 %conv, i64 0)
41+
%tobool = icmp ne i64 %expval, 0
42+
br i1 %tobool, label %if.then, label %if.else
43+
44+
if.then: ; preds = %entry
45+
%3 = load i32, i32* %rando, align 4, !tbaa !3
46+
%call2 = call i32 @baz(i32 %3)
47+
store i32 %call2, i32* %x, align 4, !tbaa !3
48+
br label %if.end
49+
50+
if.else: ; preds = %entry
51+
%call3 = call i32 @foo(i32 50)
52+
store i32 %call3, i32* %x, align 4, !tbaa !3
53+
br label %if.end
54+
55+
if.end: ; preds = %if.else, %if.then
56+
%4 = load i32, i32* %x, align 4, !tbaa !3
57+
%5 = bitcast i32* %x to i8*
58+
call void @llvm.lifetime.end.p0i8(i64 4, i8* %5) #4
59+
%6 = bitcast i32* %rando to i8*
60+
call void @llvm.lifetime.end.p0i8(i64 4, i8* %6) #4
61+
ret i32 %4
62+
}
63+
64+
; Function Attrs: argmemonly nounwind willreturn
65+
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
66+
67+
declare i32 @buzz(...) #2
68+
69+
; Function Attrs: nounwind readnone willreturn
70+
declare i64 @llvm.expect.i64(i64, i64) #3
71+
72+
declare i32 @baz(i32) #2
73+
74+
declare i32 @foo(i32) #2
75+
76+
; Function Attrs: argmemonly nounwind willreturn
77+
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
78+
79+
attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
80+
attributes #1 = { argmemonly nounwind willreturn }
81+
attributes #2 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
82+
attributes #3 = { nounwind readnone willreturn }
83+
attributes #4 = { nounwind }
84+
85+
!llvm.module.flags = !{!0, !1}
86+
!llvm.ident = !{!2}
87+
88+
!0 = !{i32 2, !"Debug Info Version", i32 3}
89+
!1 = !{i32 1, !"wchar_size", i32 4}
90+
!2 = !{!"clang version 10.0.0 (c20270bfffc9d6965219de339d66c61e9fe7d82d)"}
91+
!3 = !{!4, !4, i64 0}
92+
!4 = !{!"int", !5, i64 0}
93+
!5 = !{!"omnipotent char", !6, i64 0}
94+
!6 = !{!"Simple C/C++ TBAA"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
2+
; RUN: llvm-profdata merge %S/Inputs/misexpect-branch.proftext -o %t.profdata
3+
4+
5+
; RUN: opt < %s -lower-expect -pgo-instr-use -pgo-test-profile-file=%t.profdata -S -pgo-warn-misexpect 2>&1 | FileCheck %s --check-prefix=WARNING
6+
; RUN: opt < %s -lower-expect -pgo-instr-use -pgo-test-profile-file=%t.profdata -S -pass-remarks=misexpect 2>&1 | FileCheck %s --check-prefix=REMARK
7+
; RUN: opt < %s -lower-expect -pgo-instr-use -pgo-test-profile-file=%t.profdata -S -pgo-warn-misexpect -pass-remarks=misexpect 2>&1 | FileCheck %s --check-prefix=BOTH
8+
; RUN: opt < %s -lower-expect -pgo-instr-use -pgo-test-profile-file=%t.profdata -S 2>&1 | FileCheck %s --check-prefix=DISABLED
9+
10+
; New PM
11+
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -pgo-warn-misexpect -S 2>&1 | FileCheck %s --check-prefix=WARNING
12+
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -pass-remarks=misexpect -S 2>&1 | FileCheck %s --check-prefix=REMARK
13+
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -pgo-warn-misexpect -pass-remarks=misexpect -S 2>&1 | FileCheck %s --check-prefix=BOTH
14+
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -S 2>&1 | FileCheck %s --check-prefix=DISABLED
15+
16+
17+
18+
; WARNING-DAG: warning: <unknown>:0:0: 19.98%
19+
; WARNING-NOT: remark: <unknown>:0:0: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 19.98% (399668 / 2000000) of profiled executions.
20+
21+
; REMARK-NOT: warning: <unknown>:0:0: 19.98%
22+
; REMARK-DAG: remark: <unknown>:0:0: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 19.98% (399668 / 2000000) of profiled executions.
23+
24+
; BOTH-DAG: warning: <unknown>:0:0: 19.98%
25+
; BOTH-DAG: remark: <unknown>:0:0: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 19.98% (399668 / 2000000) of profiled executions.
26+
27+
; DISABLED-NOT: warning: <unknown>:0:0: 19.98%
28+
; DISABLED-NOT: remark: <unknown>:0:0: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 19.98% (399668 / 2000000) of profiled executions.
29+
30+
; CHECK-DAG: !{!"misexpect", i64 1, i64 2000, i64 1}
31+
32+
33+
34+
; ModuleID = 'misexpect-branch.c'
35+
source_filename = "misexpect-branch.c"
36+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
37+
target triple = "x86_64-unknown-linux-gnu"
38+
39+
@inner_loop = constant i32 100, align 4
40+
@outer_loop = constant i32 2000, align 4
41+
42+
; Function Attrs: nounwind
43+
define i32 @bar() #0 {
44+
entry:
45+
%rando = alloca i32, align 4
46+
%x = alloca i32, align 4
47+
%0 = bitcast i32* %rando to i8*
48+
call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #4
49+
%call = call i32 (...) @buzz()
50+
store i32 %call, i32* %rando, align 4, !tbaa !3
51+
%1 = bitcast i32* %x to i8*
52+
call void @llvm.lifetime.start.p0i8(i64 4, i8* %1) #4
53+
store i32 0, i32* %x, align 4, !tbaa !3
54+
%2 = load i32, i32* %rando, align 4, !tbaa !3
55+
%rem = srem i32 %2, 200000
56+
%cmp = icmp eq i32 %rem, 0
57+
%lnot = xor i1 %cmp, true
58+
%lnot1 = xor i1 %lnot, true
59+
%lnot.ext = zext i1 %lnot1 to i32
60+
%conv = sext i32 %lnot.ext to i64
61+
%expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
62+
%tobool = icmp ne i64 %expval, 0
63+
br i1 %tobool, label %if.then, label %if.else
64+
65+
if.then: ; preds = %entry
66+
%3 = load i32, i32* %rando, align 4, !tbaa !3
67+
%call2 = call i32 @baz(i32 %3)
68+
store i32 %call2, i32* %x, align 4, !tbaa !3
69+
br label %if.end
70+
71+
if.else: ; preds = %entry
72+
%call3 = call i32 @foo(i32 50)
73+
store i32 %call3, i32* %x, align 4, !tbaa !3
74+
br label %if.end
75+
76+
if.end: ; preds = %if.else, %if.then
77+
%4 = load i32, i32* %x, align 4, !tbaa !3
78+
%5 = bitcast i32* %x to i8*
79+
call void @llvm.lifetime.end.p0i8(i64 4, i8* %5) #4
80+
%6 = bitcast i32* %rando to i8*
81+
call void @llvm.lifetime.end.p0i8(i64 4, i8* %6) #4
82+
ret i32 %4
83+
}
84+
85+
; Function Attrs: argmemonly nounwind willreturn
86+
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
87+
88+
declare i32 @buzz(...) #2
89+
90+
; Function Attrs: nounwind readnone willreturn
91+
declare i64 @llvm.expect.i64(i64, i64) #3
92+
93+
declare i32 @baz(i32) #2
94+
95+
declare i32 @foo(i32) #2
96+
97+
; Function Attrs: argmemonly nounwind willreturn
98+
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
99+
100+
attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
101+
attributes #1 = { argmemonly nounwind willreturn }
102+
attributes #2 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
103+
attributes #3 = { nounwind readnone willreturn }
104+
attributes #4 = { nounwind }
105+
106+
!llvm.module.flags = !{!0, !1}
107+
!llvm.ident = !{!2}
108+
109+
!0 = !{i32 2, !"Debug Info Version", i32 3}
110+
!1 = !{i32 1, !"wchar_size", i32 4}
111+
!2 = !{!"clang version 10.0.0 (trunk c20270bfffc9d6965219de339d66c61e9fe7d82d)"}
112+
!3 = !{!4, !4, i64 0}
113+
!4 = !{!"int", !5, i64 0}
114+
!5 = !{!"omnipotent char", !6, i64 0}
115+
!6 = !{!"Simple C/C++ TBAA"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
; RUN: llvm-profdata merge %S/Inputs/misexpect-branch-correct.proftext -o %t.profdata
2+
3+
; RUN: opt < %s -lower-expect -pgo-instr-use -pgo-test-profile-file=%t.profdata -S -pgo-warn-misexpect -pass-remarks=misexpect 2>&1 | FileCheck %s
4+
5+
; New PM
6+
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -pgo-warn-misexpect -pass-remarks=misexpect -S 2>&1 | FileCheck %s
7+
8+
; CHECK-NOT: warning: {{.*}}
9+
; CHECK-NOT: remark: {{.*}}
10+
; CHECK-NOT: !"misexpect"
11+
12+
13+
; ModuleID = 'misexpect-branch-unpredictable.c'
14+
source_filename = "clang/test/Profile/misexpect-branch-unpredictable.c"
15+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
16+
target triple = "x86_64-unknown-linux-gnu"
17+
18+
@inner_loop = constant i32 100, align 4
19+
@outer_loop = constant i32 2000, align 4
20+
21+
; Function Attrs: nounwind
22+
define i32 @bar() #0 {
23+
entry:
24+
%rando = alloca i32, align 4
25+
%x = alloca i32, align 4
26+
%0 = bitcast i32* %rando to i8*
27+
call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #3
28+
%call = call i32 (...) @buzz()
29+
store i32 %call, i32* %rando, align 4, !tbaa !2
30+
%1 = bitcast i32* %x to i8*
31+
call void @llvm.lifetime.start.p0i8(i64 4, i8* %1) #3
32+
store i32 0, i32* %x, align 4, !tbaa !2
33+
%2 = load i32, i32* %rando, align 4, !tbaa !2
34+
%rem = srem i32 %2, 200000
35+
%cmp = icmp eq i32 %rem, 0
36+
%lnot = xor i1 %cmp, true
37+
%lnot1 = xor i1 %lnot, true
38+
%lnot.ext = zext i1 %lnot1 to i32
39+
%conv = sext i32 %lnot.ext to i64
40+
%tobool = icmp ne i64 %conv, 0
41+
br i1 %tobool, label %if.then, label %if.else, !unpredictable !6
42+
43+
if.then: ; preds = %entry
44+
%3 = load i32, i32* %rando, align 4, !tbaa !2
45+
%call2 = call i32 @baz(i32 %3)
46+
store i32 %call2, i32* %x, align 4, !tbaa !2
47+
br label %if.end
48+
49+
if.else: ; preds = %entry
50+
%call3 = call i32 @foo(i32 50)
51+
store i32 %call3, i32* %x, align 4, !tbaa !2
52+
br label %if.end
53+
54+
if.end: ; preds = %if.else, %if.then
55+
%4 = load i32, i32* %x, align 4, !tbaa !2
56+
%5 = bitcast i32* %x to i8*
57+
call void @llvm.lifetime.end.p0i8(i64 4, i8* %5) #3
58+
%6 = bitcast i32* %rando to i8*
59+
call void @llvm.lifetime.end.p0i8(i64 4, i8* %6) #3
60+
ret i32 %4
61+
}
62+
63+
; Function Attrs: argmemonly nounwind willreturn
64+
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
65+
66+
declare i32 @buzz(...) #2
67+
68+
declare i32 @baz(i32) #2
69+
70+
declare i32 @foo(i32) #2
71+
72+
; Function Attrs: argmemonly nounwind willreturn
73+
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
74+
75+
attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
76+
attributes #1 = { argmemonly nounwind willreturn }
77+
attributes #2 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
78+
attributes #3 = { nounwind }
79+
80+
!llvm.module.flags = !{!0}
81+
!llvm.ident = !{!1}
82+
83+
!0 = !{i32 1, !"wchar_size", i32 4}
84+
!1 = !{!"Fuchsia clang version 10.0.0 (153b453014c94291c8c6cf6320b2f46df40f26f3) (based on LLVM 10.0.0svn)"}
85+
!2 = !{!3, !3, i64 0}
86+
!3 = !{!"int", !4, i64 0}
87+
!4 = !{!"omnipotent char", !5, i64 0}
88+
!5 = !{!"Simple C/C++ TBAA"}
89+
!6 = !{}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
2+
; RUN: llvm-profdata merge %S/Inputs/misexpect-branch.proftext -o %t.profdata
3+
4+
5+
; RUN: opt < %s -lower-expect -pgo-instr-use -pgo-test-profile-file=%t.profdata -S -pgo-warn-misexpect 2>&1 | FileCheck %s --check-prefix=WARNING
6+
; RUN: opt < %s -lower-expect -pgo-instr-use -pgo-test-profile-file=%t.profdata -S -pass-remarks=misexpect 2>&1 | FileCheck %s --check-prefix=REMARK
7+
; RUN: opt < %s -lower-expect -pgo-instr-use -pgo-test-profile-file=%t.profdata -S -pgo-warn-misexpect -pass-remarks=misexpect 2>&1 | FileCheck %s --check-prefix=BOTH
8+
; RUN: opt < %s -lower-expect -pgo-instr-use -pgo-test-profile-file=%t.profdata -S 2>&1 | FileCheck %s --check-prefix=DISABLED
9+
10+
; New PM
11+
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -pgo-warn-misexpect -S 2>&1 | FileCheck %s --check-prefix=WARNING
12+
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -pass-remarks=misexpect -S 2>&1 | FileCheck %s --check-prefix=REMARK
13+
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -pgo-warn-misexpect -pass-remarks=misexpect -S 2>&1 | FileCheck %s --check-prefix=BOTH
14+
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -S 2>&1 | FileCheck %s --check-prefix=DISABLED
15+
16+
17+
18+
; WARNING-DAG: warning: misexpect-branch.c:22:0: 19.98%
19+
; WARNING-NOT: remark: misexpect-branch.c:22:0: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 19.98% (399668 / 2000000) of profiled executions.
20+
21+
; REMARK-NOT: warning: misexpect-branch.c:22:0: 19.98%
22+
; REMARK-DAG: remark: misexpect-branch.c:22:0: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 19.98% (399668 / 2000000) of profiled executions.
23+
24+
; BOTH-DAG: warning: misexpect-branch.c:22:0: 19.98%
25+
; BOTH-DAG: remark: misexpect-branch.c:22:0: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 19.98% (399668 / 2000000) of profiled executions.
26+
27+
; DISABLED-NOT: warning: misexpect-branch.c:22:0: 19.98%
28+
; DISABLED-NOT: remark: misexpect-branch.c:22:0: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 19.98% (399668 / 2000000) of profiled executions.
29+
30+
; CHECK-DAG: !{!"misexpect", i64 1, i64 2000, i64 1}
31+
32+
33+
34+
; ModuleID = 'misexpect-branch.c'
35+
source_filename = "misexpect-branch.c"
36+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
37+
target triple = "x86_64-unknown-linux-gnu"
38+
39+
@inner_loop = constant i32 100, align 4
40+
@outer_loop = constant i32 2000, align 4
41+
42+
; Function Attrs: nounwind
43+
define i32 @bar() #0 !dbg !6 {
44+
entry:
45+
%rando = alloca i32, align 4
46+
%x = alloca i32, align 4
47+
%0 = bitcast i32* %rando to i8*, !dbg !9
48+
call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #4, !dbg !9
49+
%call = call i32 (...) @buzz(), !dbg !9
50+
store i32 %call, i32* %rando, align 4, !dbg !9, !tbaa !10
51+
%1 = bitcast i32* %x to i8*, !dbg !14
52+
call void @llvm.lifetime.start.p0i8(i64 4, i8* %1) #4, !dbg !14
53+
store i32 0, i32* %x, align 4, !dbg !14, !tbaa !10
54+
%2 = load i32, i32* %rando, align 4, !dbg !15, !tbaa !10
55+
%rem = srem i32 %2, 200000, !dbg !15
56+
%cmp = icmp eq i32 %rem, 0, !dbg !15
57+
%lnot = xor i1 %cmp, true, !dbg !15
58+
%lnot1 = xor i1 %lnot, true, !dbg !15
59+
%lnot.ext = zext i1 %lnot1 to i32, !dbg !15
60+
%conv = sext i32 %lnot.ext to i64, !dbg !15
61+
%expval = call i64 @llvm.expect.i64(i64 %conv, i64 1), !dbg !15
62+
%tobool = icmp ne i64 %expval, 0, !dbg !15
63+
br i1 %tobool, label %if.then, label %if.else, !dbg !15
64+
65+
if.then: ; preds = %entry
66+
%3 = load i32, i32* %rando, align 4, !dbg !16, !tbaa !10
67+
%call2 = call i32 @baz(i32 %3), !dbg !16
68+
store i32 %call2, i32* %x, align 4, !dbg !16, !tbaa !10
69+
br label %if.end, !dbg !17
70+
71+
if.else: ; preds = %entry
72+
%call3 = call i32 @foo(i32 50), !dbg !18
73+
store i32 %call3, i32* %x, align 4, !dbg !18, !tbaa !10
74+
br label %if.end
75+
76+
if.end: ; preds = %if.else, %if.then
77+
%4 = load i32, i32* %x, align 4, !dbg !19, !tbaa !10
78+
%5 = bitcast i32* %x to i8*, !dbg !20
79+
call void @llvm.lifetime.end.p0i8(i64 4, i8* %5) #4, !dbg !20
80+
%6 = bitcast i32* %rando to i8*, !dbg !20
81+
call void @llvm.lifetime.end.p0i8(i64 4, i8* %6) #4, !dbg !20
82+
ret i32 %4, !dbg !19
83+
}
84+
85+
; Function Attrs: argmemonly nounwind willreturn
86+
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
87+
88+
declare i32 @buzz(...) #2
89+
90+
; Function Attrs: nounwind readnone willreturn
91+
declare i64 @llvm.expect.i64(i64, i64) #3
92+
93+
declare i32 @baz(i32) #2
94+
95+
declare i32 @foo(i32) #2
96+
97+
; Function Attrs: argmemonly nounwind willreturn
98+
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
99+
100+
attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
101+
attributes #1 = { argmemonly nounwind willreturn }
102+
attributes #2 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
103+
attributes #3 = { nounwind readnone willreturn }
104+
attributes #4 = { nounwind }
105+
106+
!llvm.dbg.cu = !{!0}
107+
!llvm.module.flags = !{!3, !4}
108+
!llvm.ident = !{!5}
109+
110+
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (trunk c20270bfffc9d6965219de339d66c61e9fe7d82d)", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, enums: !2, nameTableKind: None)
111+
!1 = !DIFile(filename: "<stdin>", directory: ".")
112+
!2 = !{}
113+
!3 = !{i32 2, !"Debug Info Version", i32 3}
114+
!4 = !{i32 1, !"wchar_size", i32 4}
115+
!5 = !{!"clang version 10.0.0 (trunk c20270bfffc9d6965219de339d66c61e9fe7d82d)"}
116+
!6 = distinct !DISubprogram(name: "bar", scope: !7, file: !7, line: 19, type: !8, scopeLine: 19, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
117+
!7 = !DIFile(filename: "misexpect-branch.c", directory: ".")
118+
!8 = !DISubroutineType(types: !2)
119+
!9 = !DILocation(line: 20, scope: !6)
120+
!10 = !{!11, !11, i64 0}
121+
!11 = !{!"int", !12, i64 0}
122+
!12 = !{!"omnipotent char", !13, i64 0}
123+
!13 = !{!"Simple C/C++ TBAA"}
124+
!14 = !DILocation(line: 21, scope: !6)
125+
!15 = !DILocation(line: 22, scope: !6)
126+
!16 = !DILocation(line: 23, scope: !6)
127+
!17 = !DILocation(line: 24, scope: !6)
128+
!18 = !DILocation(line: 25, scope: !6)
129+
!19 = !DILocation(line: 27, scope: !6)
130+
!20 = !DILocation(line: 28, scope: !6)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
2+
; RUN: llvm-profdata merge %S/Inputs/misexpect-switch.proftext -o %t.profdata
3+
4+
; RUN: opt < %s -lower-expect -pgo-instr-use -pgo-test-profile-file=%t.profdata -S -pgo-warn-misexpect 2>&1 | FileCheck %s --check-prefix=WARNING
5+
; RUN: opt < %s -lower-expect -pgo-instr-use -pgo-test-profile-file=%t.profdata -S -pass-remarks=misexpect 2>&1 | FileCheck %s --check-prefix=REMARK
6+
; RUN: opt < %s -lower-expect -pgo-instr-use -pgo-test-profile-file=%t.profdata -S -pgo-warn-misexpect -pass-remarks=misexpect 2>&1 | FileCheck %s --check-prefix=BOTH
7+
; RUN: opt < %s -lower-expect -pgo-instr-use -pgo-test-profile-file=%t.profdata -S 2>&1 | FileCheck %s --check-prefix=DISABLED
8+
9+
; New PM
10+
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -pgo-warn-misexpect -S 2>&1 | FileCheck %s --check-prefix=WARNING
11+
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -pass-remarks=misexpect -S 2>&1 | FileCheck %s --check-prefix=REMARK
12+
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -pgo-warn-misexpect -pass-remarks=misexpect -S 2>&1 | FileCheck %s --check-prefix=BOTH
13+
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -S 2>&1 | FileCheck %s --check-prefix=DISABLED
14+
15+
; WARNING-DAG: warning: <unknown>:0:0: 0.00%
16+
; WARNING-NOT: remark: <unknown>:0:0: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 0.00% (0 / 27943) of profiled executions.
17+
18+
; REMARK-NOT: warning: <unknown>:0:0: 0.00%
19+
; REMARK-DAG: remark: <unknown>:0:0: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 0.00% (0 / 27943) of profiled executions.
20+
21+
; BOTH-DAG: warning: <unknown>:0:0: 0.00%
22+
; BOTH-DAG: remark: <unknown>:0:0: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 0.00% (0 / 27943) of profiled executions.
23+
24+
; DISABLED-NOT: warning: <unknown>:0:0: 0.00%
25+
; DISABLED-NOT: remark: <unknown>:0:0: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 0.00% (0 / 27943) of profiled executions.
26+
27+
; DISABLED-NOT: warning: <unknown>:0:0: 0.00%
28+
; DISABLED-NOT: remark: <unknown>:0:0: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 0.00% (0 / 27943) of profiled executions.
29+
30+
; CORRECT-NOT: warning: {{.*}}
31+
; CORRECT-NOT: remark: {{.*}}
32+
; CHECK-DAG: !{!"misexpect", i64 0, i64 2000, i64 1}
33+
34+
35+
36+
; ModuleID = 'misexpect-switch.c'
37+
source_filename = "misexpect-switch.c"
38+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
39+
target triple = "x86_64-unknown-linux-gnu"
40+
41+
@inner_loop = dso_local constant i32 1000, align 4
42+
@outer_loop = dso_local constant i32 20, align 4
43+
@arry_size = dso_local constant i32 25, align 4
44+
@arry = dso_local global [25 x i32] zeroinitializer, align 16
45+
46+
; Function Attrs: nounwind uwtable
47+
define dso_local void @init_arry() #0 {
48+
entry:
49+
%i = alloca i32, align 4
50+
%0 = bitcast i32* %i to i8*
51+
call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #6
52+
store i32 0, i32* %i, align 4, !tbaa !4
53+
br label %for.cond
54+
55+
for.cond: ; preds = %for.inc, %entry
56+
%1 = load i32, i32* %i, align 4, !tbaa !4
57+
%cmp = icmp slt i32 %1, 25
58+
br i1 %cmp, label %for.body, label %for.end
59+
60+
for.body: ; preds = %for.cond
61+
%call = call i32 @rand() #6
62+
%rem = srem i32 %call, 10
63+
%2 = load i32, i32* %i, align 4, !tbaa !4
64+
%idxprom = sext i32 %2 to i64
65+
%arrayidx = getelementptr inbounds [25 x i32], [25 x i32]* @arry, i64 0, i64 %idxprom
66+
store i32 %rem, i32* %arrayidx, align 4, !tbaa !4
67+
br label %for.inc
68+
69+
for.inc: ; preds = %for.body
70+
%3 = load i32, i32* %i, align 4, !tbaa !4
71+
%inc = add nsw i32 %3, 1
72+
store i32 %inc, i32* %i, align 4, !tbaa !4
73+
br label %for.cond
74+
75+
for.end: ; preds = %for.cond
76+
%4 = bitcast i32* %i to i8*
77+
call void @llvm.lifetime.end.p0i8(i64 4, i8* %4) #6
78+
ret void
79+
}
80+
81+
; Function Attrs: argmemonly nounwind willreturn
82+
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
83+
84+
; Function Attrs: nounwind readnone speculatable willreturn
85+
declare void @llvm.dbg.declare(metadata, metadata, metadata) #2
86+
87+
; Function Attrs: nounwind
88+
declare dso_local i32 @rand() #3
89+
90+
; Function Attrs: argmemonly nounwind willreturn
91+
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
92+
93+
; Function Attrs: nounwind uwtable
94+
define dso_local i32 @main() #0 {
95+
entry:
96+
%retval = alloca i32, align 4
97+
%val = alloca i32, align 4
98+
%j = alloca i32, align 4
99+
%condition = alloca i32, align 4
100+
store i32 0, i32* %retval, align 4
101+
call void @init_arry()
102+
%0 = bitcast i32* %val to i8*
103+
call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #6
104+
store i32 0, i32* %val, align 4, !tbaa !4
105+
%1 = bitcast i32* %j to i8*
106+
call void @llvm.lifetime.start.p0i8(i64 4, i8* %1) #6
107+
store i32 0, i32* %j, align 4, !tbaa !4
108+
br label %for.cond
109+
110+
for.cond: ; preds = %for.inc, %entry
111+
%2 = load i32, i32* %j, align 4, !tbaa !4
112+
%cmp = icmp slt i32 %2, 20000
113+
br i1 %cmp, label %for.body, label %for.end
114+
115+
for.body: ; preds = %for.cond
116+
%3 = bitcast i32* %condition to i8*
117+
call void @llvm.lifetime.start.p0i8(i64 4, i8* %3) #6
118+
%call = call i32 @rand() #6
119+
%rem = srem i32 %call, 5
120+
store i32 %rem, i32* %condition, align 4, !tbaa !4
121+
%4 = load i32, i32* %condition, align 4, !tbaa !4
122+
%conv = zext i32 %4 to i64
123+
%expval = call i64 @llvm.expect.i64(i64 %conv, i64 6)
124+
switch i64 %expval, label %sw.default [
125+
i64 0, label %sw.bb
126+
i64 1, label %sw.bb2
127+
i64 2, label %sw.bb2
128+
i64 3, label %sw.bb2
129+
i64 4, label %sw.bb3
130+
]
131+
132+
sw.bb: ; preds = %for.body
133+
%call1 = call i32 @sum(i32* getelementptr inbounds ([25 x i32], [25 x i32]* @arry, i64 0, i64 0), i32 25)
134+
%5 = load i32, i32* %val, align 4, !tbaa !4
135+
%add = add nsw i32 %5, %call1
136+
store i32 %add, i32* %val, align 4, !tbaa !4
137+
br label %sw.epilog
138+
139+
sw.bb2: ; preds = %for.body, %for.body, %for.body
140+
br label %sw.epilog
141+
142+
sw.bb3: ; preds = %for.body
143+
%call4 = call i32 @random_sample(i32* getelementptr inbounds ([25 x i32], [25 x i32]* @arry, i64 0, i64 0), i32 25)
144+
%6 = load i32, i32* %val, align 4, !tbaa !4
145+
%add5 = add nsw i32 %6, %call4
146+
store i32 %add5, i32* %val, align 4, !tbaa !4
147+
br label %sw.epilog
148+
149+
sw.default: ; preds = %for.body
150+
unreachable
151+
152+
sw.epilog: ; preds = %sw.bb3, %sw.bb2, %sw.bb
153+
%7 = bitcast i32* %condition to i8*
154+
call void @llvm.lifetime.end.p0i8(i64 4, i8* %7) #6
155+
br label %for.inc
156+
157+
for.inc: ; preds = %sw.epilog
158+
%8 = load i32, i32* %j, align 4, !tbaa !4
159+
%inc = add nsw i32 %8, 1
160+
store i32 %inc, i32* %j, align 4, !tbaa !4
161+
br label %for.cond
162+
163+
for.end: ; preds = %for.cond
164+
%9 = bitcast i32* %j to i8*
165+
call void @llvm.lifetime.end.p0i8(i64 4, i8* %9) #6
166+
%10 = bitcast i32* %val to i8*
167+
call void @llvm.lifetime.end.p0i8(i64 4, i8* %10) #6
168+
ret i32 0
169+
}
170+
171+
; Function Attrs: nounwind readnone willreturn
172+
declare i64 @llvm.expect.i64(i64, i64) #4
173+
174+
declare dso_local i32 @sum(i32*, i32) #5
175+
176+
declare dso_local i32 @random_sample(i32*, i32) #5
177+
178+
attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
179+
attributes #1 = { argmemonly nounwind willreturn }
180+
attributes #2 = { nounwind readnone speculatable willreturn }
181+
attributes #3 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
182+
attributes #4 = { nounwind readnone willreturn }
183+
attributes #5 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
184+
attributes #6 = { nounwind }
185+
186+
!llvm.module.flags = !{!0, !1, !2}
187+
!llvm.ident = !{!3}
188+
189+
!0 = !{i32 2, !"Dwarf Version", i32 4}
190+
!1 = !{i32 2, !"Debug Info Version", i32 3}
191+
!2 = !{i32 1, !"wchar_size", i32 4}
192+
!3 = !{!"clang version 10.0.0 (60b79b85b1763d3d25630261e5cd1adb7f0835bc)"}
193+
!4 = !{!5, !5, i64 0}
194+
!5 = !{!"int", !6, i64 0}
195+
!6 = !{!"omnipotent char", !7, i64 0}
196+
!7 = !{!"Simple C/C++ TBAA"}

‎llvm/test/Transforms/PGOProfile/misexpect-switch.ll

+293
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.