[misexpect] Re-implement MisExpect Diagnostics

Reimplements MisExpect diagnostics from D66324 to reconstruct its
original checking methodology only using MD_prof branch_weights
metadata.

New checks rely on 2 invariants:

1) For frontend instrumentation, MD_prof branch_weights will always be
   populated before llvm.expect intrinsics are lowered.

2) for IR and sample profiling, llvm.expect intrinsics will always be
   lowered before branch_weights are populated from the IR profiles.

These invariants allow the checking to assume how the existing branch
weights are populated depending on the profiling method used, and emit
the correct diagnostics. If these invariants are ever invalidated, the
MisExpect related checks would need to be updated, potentially by
re-introducing MD_misexpect metadata, and ensuring it always will be
transformed the same way as branch_weights in other optimization passes.

Frontend based profiling is now enabled without using LLVM Args, by
introducing a new CodeGen option, and checking if the -Wmisexpect flag
has been passed on the command line.

Reviewed By: tejohnson

Differential Revision: https://reviews.llvm.org/D115907
This commit is contained in:
Paul Kirth 2022-03-19 00:54:23 +00:00
parent 42d3d717b8
commit 2add3fbd97
49 changed files with 2043 additions and 2 deletions

75
clang/docs/MisExpect.rst Normal file
View File

@ -0,0 +1,75 @@
===================
Misexpect
===================
.. contents::
.. toctree::
:maxdepth: 1
When developers use ``llvm.expect`` intrinsics, i.e., through use of
``__builtin_expect(...)``, they are trying to communicate how their code is
expected to behave at runtime to the optimizer. These annotations, however, can
be incorrect for a variety of reasons: changes to the code base invalidate them
silently, the developer mis-annotated them (e.g., using ``LIKELY`` instead of
``UNLIKELY``), or perhaps they assumed something incorrectly when they wrote
the annotation. Regardless of why, it is useful to detect these situations so
that the optimizer can make more useful decisions about the code.
MisExpect diagnostics are intended to help developers identify and address
these situations, by comparing the branch weights added by the ``llvm.expect``
intrinsic to those collected through profiling. Whenever these values are
mismatched, a diagnostic is surfaced to the user. Details on how the checks
operate in the LLVM backed can be found in LLVM's documentation.
By default MisExpect checking is quite strict, because the use of the
``llvm.expect`` intrinsic is designed for specialized cases, where the outcome
of a condition is severely skewed. As a result, the optimizer can be extremely
aggressive, which can result in performance degradation if the outcome is less
predictable than the annotation suggests. Even when the annotation is correct
90% of the time, it may be beneficial to either remove the annotation or to use
a different intrinsic that can communicate the probability more directly.
Because this may be too strict, MisExpect diagnostics are not enabled by
default, and support an additional flag to tolerate some deviation from the
exact thresholds. The ``-fdiagnostic-misexpect-tolerance=N`` accepts
deviations when comparing branch weights within ``N%`` of the expected values.
So passing ``-fdiagnostic-misexpect-tolerance=5`` will not report diagnostic messages
if the branch weight from the profile is within 5% of the weight added by
the ``llvm.expect`` intrinsic.
MisExpect diagnostics are also available in the form of optimization remarks,
which can be serialized and processed through the ``opt-viewer.py``
scripts in LLVM.
.. option:: -Rpass=misexpect
Enables optimization remarks for misexpect when profiling data conflicts with
use of ``llvm.expect`` intrinsics.
.. option:: -Wmisexpect
Enables misexpect warnings when profiling data conflicts with use of
``llvm.expect`` intrinsics.
.. option:: -fdiagnostic-misexpect-tolerance=N
Relaxes misexpect checking to tolerate profiling values within N% of the
expected branch weight. e.g., a value of ``N=5`` allows misexpect to check against
``0.95 * Threshold``
LLVM supports 4 types of profile formats: Frontend, IR, CS-IR, and
Sampling. MisExpect Diagnostics are compatible with all Profiling formats.
+----------------+--------------------------------------------------------------------------------------+
| Profile Type | Description |
+================+======================================================================================+
| Frontend | Profiling instrumentation added during compilation by the frontend, i.e. ``clang`` |
+----------------+--------------------------------------------------------------------------------------+
| IR | Profiling instrumentation added during by the LLVM backend |
+----------------+--------------------------------------------------------------------------------------+
| CS-IR | Context Sensitive IR based profiles |
+----------------+--------------------------------------------------------------------------------------+
| Sampling | Profiles collected through sampling with external tools, such as ``perf`` on Linux |
+----------------+--------------------------------------------------------------------------------------+

View File

@ -105,6 +105,9 @@ Improvements to Clang's diagnostics
- ``-Wunused-but-set-variable`` now also warns if the variable is only used
by unary operators.
- ``-Wmisexpect`` warns when the branch weights collected during profiling
conflict with those added by ``llvm.expect``.
Non-comprehensive list of changes in this release
-------------------------------------------------
- The builtin function __builtin_dump_struct would crash clang when the target

View File

@ -175,6 +175,7 @@ CODEGENOPT(NoExecStack , 1, 0) ///< Set when -Wa,--noexecstack is enabled.
CODEGENOPT(FatalWarnings , 1, 0) ///< Set when -Wa,--fatal-warnings is
///< enabled.
CODEGENOPT(NoWarn , 1, 0) ///< Set when -Wa,--no-warn is enabled.
CODEGENOPT(MisExpect , 1, 0) ///< Set when -Wmisexpect is enabled
CODEGENOPT(EnableSegmentedStacks , 1, 0) ///< Set when -fsplit-stack is enabled.
CODEGENOPT(NoInlineLineTables, 1, 0) ///< Whether debug info should contain
///< inline line tables.

View File

@ -420,6 +420,10 @@ public:
/// If threshold option is not specified, it is disabled by default.
Optional<uint64_t> DiagnosticsHotnessThreshold = 0;
/// The maximum percentage profiling weights can deviate from the expected
/// values in order to be included in misexpect diagnostics.
Optional<uint64_t> DiagnosticsMisExpectTolerance = 0;
public:
// Define accessors/mutators for code generation options of enumeration type.
#define CODEGENOPT(Name, Bits, Default)

View File

@ -149,6 +149,8 @@ def err_drv_invalid_darwin_version : Error<
"invalid Darwin version number: %0">;
def err_drv_invalid_diagnotics_hotness_threshold : Error<
"invalid argument in '%0', only integer or 'auto' is supported">;
def err_drv_invalid_diagnotics_misexpect_tolerance : Error<
"invalid argument in '%0', only integers are supported">;
def err_drv_missing_argument : Error<
"argument to '%0' is missing (expected %1 value%s1)">;
def err_drv_invalid_Xarch_argument_with_args : Error<
@ -376,6 +378,9 @@ def warn_drv_empty_joined_argument : Warning<
def warn_drv_diagnostics_hotness_requires_pgo : Warning<
"argument '%0' requires profile-guided optimization information">,
InGroup<UnusedCommandLineArgument>;
def warn_drv_diagnostics_misexpect_requires_pgo : Warning<
"argument '%0' requires profile-guided optimization information">,
InGroup<UnusedCommandLineArgument>;
def warn_drv_clang_unsupported : Warning<
"the clang compiler does not support '%0'">;
def warn_drv_deprecated_arg : Warning<

View File

@ -311,6 +311,11 @@ def warn_profile_data_missing : Warning<
def warn_profile_data_unprofiled : Warning<
"no profile data available for file \"%0\"">,
InGroup<ProfileInstrUnprofiled>;
def warn_profile_data_misexpect : Warning<
"Potential performance regression from use of __builtin_expect(): "
"Annotation was correct on %0 of profiled executions.">,
BackendInfo,
InGroup<MisExpect>;
} // end of instrumentation issue category
}

View File

@ -1254,6 +1254,7 @@ def BackendWarningAttributes : DiagGroup<"attribute-warning">;
def ProfileInstrMissing : DiagGroup<"profile-instr-missing">;
def ProfileInstrOutOfDate : DiagGroup<"profile-instr-out-of-date">;
def ProfileInstrUnprofiled : DiagGroup<"profile-instr-unprofiled">;
def MisExpect : DiagGroup<"misexpect">;
// AddressSanitizer frontend instrumentation remarks.
def SanitizeAddressRemarks : DiagGroup<"sanitize-address">;

View File

@ -1426,6 +1426,9 @@ def fdiagnostics_hotness_threshold_EQ : Joined<["-"], "fdiagnostics-hotness-thre
Group<f_Group>, Flags<[CC1Option]>, MetaVarName<"<value>">,
HelpText<"Prevent optimization remarks from being output if they do not have at least this profile count. "
"Use 'auto' to apply the threshold from profile summary">;
def fdiagnostics_misexpect_tolerance_EQ : Joined<["-"], "fdiagnostics-misexpect-tolerance=">,
Group<f_Group>, Flags<[CC1Option]>, MetaVarName<"<value>">,
HelpText<"Prevent misexpect diagnostics from being output if the profile counts are within N% of the expected. ">;
defm diagnostics_show_option : BoolFOption<"diagnostics-show-option",
DiagnosticOpts<"ShowOptionNames">, DefaultTrue,
NegFlag<SetFalse, [CC1Option]>, PosFlag<SetTrue, [], "Print option name with mappable diagnostics">>;

View File

@ -650,6 +650,7 @@ static bool initTargetOptions(DiagnosticsEngine &Diags,
Entry.IgnoreSysRoot ? Entry.Path : HSOpts.Sysroot + Entry.Path);
Options.MCOptions.Argv0 = CodeGenOpts.Argv0;
Options.MCOptions.CommandLineArgs = CodeGenOpts.CommandLineArgs;
Options.MisExpect = CodeGenOpts.MisExpect;
return true;
}

View File

@ -340,6 +340,15 @@ namespace clang {
CodeGenOpts.getProfileUse() != CodeGenOptions::ProfileNone)
Ctx.setDiagnosticsHotnessRequested(true);
if (CodeGenOpts.MisExpect) {
Ctx.setMisExpectWarningRequested(true);
}
if (CodeGenOpts.DiagnosticsMisExpectTolerance) {
Ctx.setDiagnosticsMisExpectTolerance(
CodeGenOpts.DiagnosticsMisExpectTolerance);
}
// Link each LinkModule into our module.
if (LinkInModules())
return;
@ -440,6 +449,9 @@ namespace clang {
void OptimizationFailureHandler(
const llvm::DiagnosticInfoOptimizationFailure &D);
void DontCallDiagHandler(const DiagnosticInfoDontCall &D);
/// Specialized handler for misexpect warnings.
/// Note that misexpect remarks are emitted through ORE
void MisExpectDiagHandler(const llvm::DiagnosticInfoMisExpect &D);
};
void BackendConsumer::anchor() {}
@ -821,6 +833,25 @@ void BackendConsumer::DontCallDiagHandler(const DiagnosticInfoDontCall &D) {
<< llvm::demangle(D.getFunctionName().str()) << D.getNote();
}
void BackendConsumer::MisExpectDiagHandler(
const llvm::DiagnosticInfoMisExpect &D) {
StringRef Filename;
unsigned Line, Column;
bool BadDebugInfo = false;
FullSourceLoc Loc =
getBestLocationFromDebugLoc(D, BadDebugInfo, Filename, Line, Column);
Diags.Report(Loc, diag::warn_profile_data_misexpect) << D.getMsg().str();
if (BadDebugInfo)
// If we were not able to translate the file:line:col information
// back to a SourceLocation, at least emit a note stating that
// we could not translate this location. This can happen in the
// case of #line directives.
Diags.Report(Loc, diag::note_fe_backend_invalid_loc)
<< Filename << Line << Column;
}
/// This function is invoked when the backend needs
/// to report something to the user.
void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) {
@ -895,6 +926,9 @@ void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) {
case llvm::DK_DontCall:
DontCallDiagHandler(cast<DiagnosticInfoDontCall>(DI));
return;
case llvm::DK_MisExpect:
MisExpectDiagHandler(cast<DiagnosticInfoMisExpect>(DI));
return;
default:
// Plugin IDs are not bound to any value as they are set dynamically.
ComputeDiagRemarkID(Severity, backend_plugin, DiagID);

View File

@ -106,6 +106,20 @@ using namespace driver;
using namespace options;
using namespace llvm::opt;
//===----------------------------------------------------------------------===//
// Helpers.
//===----------------------------------------------------------------------===//
// Parse misexpect tolerance argument value.
// Valid option values are integers in the range [0, 100)
inline Expected<Optional<uint64_t>> parseToleranceOption(StringRef Arg) {
int64_t Val;
if (Arg.getAsInteger(10, Val))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Not an integer: %s", Arg.data());
return Val;
}
//===----------------------------------------------------------------------===//
// Initialization.
//===----------------------------------------------------------------------===//
@ -1535,6 +1549,9 @@ void CompilerInvocation::GenerateCodeGenArgs(
: "auto",
SA);
GenerateArg(Args, OPT_fdiagnostics_misexpect_tolerance_EQ,
Twine(*Opts.DiagnosticsMisExpectTolerance), SA);
for (StringRef Sanitizer : serializeSanitizerKinds(Opts.SanitizeRecover))
GenerateArg(Args, OPT_fsanitize_recover_EQ, Sanitizer, SA);
@ -1952,6 +1969,23 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
}
}
if (auto *arg =
Args.getLastArg(options::OPT_fdiagnostics_misexpect_tolerance_EQ)) {
auto ResultOrErr = parseToleranceOption(arg->getValue());
if (!ResultOrErr) {
Diags.Report(diag::err_drv_invalid_diagnotics_misexpect_tolerance)
<< "-fdiagnostics-misexpect-tolerance=";
} else {
Opts.DiagnosticsMisExpectTolerance = *ResultOrErr;
if ((!Opts.DiagnosticsMisExpectTolerance.hasValue() ||
Opts.DiagnosticsMisExpectTolerance.getValue() > 0) &&
!UsingProfile)
Diags.Report(diag::warn_drv_diagnostics_misexpect_requires_pgo)
<< "-fdiagnostics-misexpect-tolerance=";
}
}
// If the user requested to use a sample profile for PGO, then the
// backend will need to track source location information so the profile
// can be incorporated into the IR.
@ -4577,6 +4611,13 @@ bool CompilerInvocation::CreateFromArgsImpl(
if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC)
LangOpts.ObjCExceptions = 1;
for (auto Warning : Res.getDiagnosticOpts().Warnings) {
if (Warning == "misexpect" &&
!Diags.isIgnored(diag::warn_profile_data_misexpect, SourceLocation())) {
Res.getCodeGenOpts().MisExpect = true;
}
}
if (LangOpts.CUDA) {
// During CUDA device-side compilation, the aux triple is the
// triple used for host compilation.

View File

@ -0,0 +1,8 @@
bar
# Func Hash:
11262309464
# Num Counters:
2
# Counter Values:
200000
2

View File

@ -0,0 +1,17 @@
bar
# Func Hash:
45795613684824
# Num Counters:
2
# Counter Values:
200000
180000
fizz
# Func Hash:
45795613684824
# Num Counters:
2
# Counter Values:
200000
18000

View File

@ -0,0 +1,12 @@
main
# Func Hash:
79676873694057560
# Num Counters:
5
# Counter Values:
1
20
20000
20000
20000

View File

@ -0,0 +1,16 @@
main
# Func Hash:
8734802134600123338
# Num Counters:
9
# Counter Values:
1
20000
20000
4066
11889
0
0
4045
0

View File

@ -0,0 +1,16 @@
main
# Func Hash:
3721743393642630379
# Num Counters:
10
# Counter Values:
1
20
20000
20000
1
0
0
0
19999
0

View File

@ -0,0 +1,32 @@
main
# Func Hash:
872687477373597607
# Num Counters:
9
# Counter Values:
1
20
20000
20000
3
3
1
3
19990
random_sample
# Func Hash:
24
# Num Counters:
1
# Counter Values:
19990
sum
# Func Hash:
24
# Num Counters:
1
# Counter Values:
3

View File

@ -0,0 +1,26 @@
// Test that misexpect emits no warning when prediction is correct
// RUN: llvm-profdata merge %S/Inputs/misexpect-branch.proftext -o %t.profdata
// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -Wmisexpect
// expected-no-diagnostics
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
int foo(int);
int baz(int);
int buzz();
const int inner_loop = 100;
const int outer_loop = 2000;
int bar() {
int rando = buzz();
int x = 0;
if (unlikely(rando % (outer_loop * inner_loop) == 0)) {
x = baz(rando);
} else {
x = foo(50);
}
return x;
}

View File

@ -0,0 +1,23 @@
// Test that misexpect emits no warning when condition is not a compile-time constant
// RUN: llvm-profdata merge %S/Inputs/misexpect-branch-nonconst-expect-arg.proftext -o %t.profdata
// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -Wmisexpect
// expected-no-diagnostics
int foo(int);
int baz(int);
int buzz();
const int inner_loop = 100;
const int outer_loop = 2000;
int bar() {
int rando = buzz();
int x = 0;
if (__builtin_expect(rando % (outer_loop * inner_loop) == 0, buzz())) {
x = baz(rando);
} else {
x = foo(50);
}
return x;
}

View File

@ -0,0 +1,25 @@
// Test that misexpect emits no warning when prediction is correct
// RUN: llvm-profdata merge %S/Inputs/misexpect-branch.proftext -o %t.profdata
// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -Wmisexpect
// expected-no-diagnostics
#define unpredictable(x) __builtin_unpredictable(!!(x))
int foo(int);
int baz(int);
int buzz();
const int inner_loop = 100;
const int outer_loop = 2000;
int bar() {
int rando = buzz();
int x = 0;
if (unpredictable(rando % (outer_loop * inner_loop) == 0)) {
x = baz(rando);
} else {
x = foo(50);
}
return x;
}

View File

@ -0,0 +1,49 @@
// Test that misexpect detects mis-annotated branches
// test diagnostics are issued when profiling data mis-matches annotations
// RUN: llvm-profdata merge %S/Inputs/misexpect-branch.proftext -o %t.profdata
// RUN: %clang_cc1 %s -O2 -o - -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify=imprecise -Wmisexpect
// RUN: %clang_cc1 %s -O2 -o - -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify=exact -Wmisexpect -debug-info-kind=line-tables-only
// there should be no diagnostics when the tolerance is sufficiently high, or when -Wmisexpect is not requested
// RUN: %clang_cc1 %s -O2 -o - -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify=foo -fdiagnostics-misexpect-tolerance=10 -Wmisexpect -debug-info-kind=line-tables-only
// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify=foo
// Ensure we emit an error when we don't use pgo with tolerance threshold
// RUN: %clang_cc1 %s -O2 -o - -emit-llvm -fdiagnostics-misexpect-tolerance=10 -Wmisexpect -debug-info-kind=line-tables-only 2>&1 | FileCheck -check-prefix=NO_PGO %s
// Test -fdiagnostics-misexpect-tolerance= requires pgo profile
// NO_PGO: '-fdiagnostics-misexpect-tolerance=' requires profile-guided optimization information
// foo-no-diagnostics
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
int foo(int);
int baz(int);
int buzz();
const int inner_loop = 100;
const int outer_loop = 2000;
int bar() { // imprecise-warning-re {{Potential performance regression from use of __builtin_expect(): Annotation was correct on {{.+}}% ({{[0-9]+ / [0-9]+}}) of profiled executions.}}
int rando = buzz();
int x = 0;
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.}}
x = baz(rando);
} else {
x = foo(50);
}
return x;
}
int fizz() { // imprecise-warning-re {{Potential performance regression from use of __builtin_expect(): Annotation was correct on {{.+}}% ({{[0-9]+ / [0-9]+}}) of profiled executions.}}
int rando = buzz();
int x = 0;
if (unlikely(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.}}
x = baz(rando);
} else {
x = foo(50);
}
return x;
}

View File

@ -0,0 +1,40 @@
// Test that misexpect detects mis-annotated switch statements for default case
// RUN: llvm-profdata merge %S/Inputs/misexpect-switch-default.proftext -o %t.profdata
// RUN: %clang_cc1 %s -O2 -o - -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -Wmisexpect -debug-info-kind=line-tables-only
#define inner_loop 1000
#define outer_loop 20
#define arry_size 25
int sum(int *buff, int size);
int random_sample(int *buff, int size);
int rand();
void init_arry();
int arry[arry_size] = {0};
int main() {
init_arry();
int val = 0;
int j;
for (j = 0; j < outer_loop * inner_loop; ++j) {
unsigned condition = rand() % 5;
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.}}
case 0:
val += sum(arry, arry_size);
break;
case 1:
case 2:
case 3:
break;
case 4:
val += random_sample(arry, arry_size);
break;
default:
__builtin_unreachable();
} // end switch
} // end outer_loop
return 0;
}

View File

@ -0,0 +1,44 @@
// Test that misexpect emits no warning when switch condition is non-const
// RUN: llvm-profdata merge %S/Inputs/misexpect-switch-nonconst.proftext -o %t.profdata
// RUN: %clang_cc1 %s -O2 -o - -disable-llvm-passes -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify
// expected-no-diagnostics
#define inner_loop 1000
#define outer_loop 20
#define arry_size 25
int sum(int *buff, int size);
int random_sample(int *buff, int size);
int rand();
void init_arry();
int arry[arry_size] = {0};
int main() {
init_arry();
int val = 0;
int j, k;
for (j = 0; j < outer_loop; ++j) {
for (k = 0; k < inner_loop; ++k) {
unsigned condition = rand() % 10000;
switch (__builtin_expect(condition, rand())) {
case 0:
val += sum(arry, arry_size);
break;
case 1:
case 2:
case 3:
case 4:
val += random_sample(arry, arry_size);
break;
default:
__builtin_unreachable();
} // end switch
} // end inner_loop
} // end outer_loop
return 0;
}

View File

@ -0,0 +1,36 @@
// Test that misexpect emits no warning when there is only one switch case
// RUN: llvm-profdata merge %S/Inputs/misexpect-switch-default-only.proftext -o %t.profdata
// RUN: %clang_cc1 %s -O2 -o - -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -Wmisexpect -debug-info-kind=line-tables-only
// expected-no-diagnostics
#define inner_loop 1000
#define outer_loop 20
#define arry_size 25
int sum(int *buff, int size);
int random_sample(int *buff, int size);
int rand();
void init_arry();
int arry[arry_size] = {0};
int main() {
init_arry();
int val = 0;
int j, k;
for (j = 0; j < outer_loop; ++j) {
for (k = 0; k < inner_loop; ++k) {
unsigned condition = rand() % 10000;
switch (__builtin_expect(condition, 0)) {
default:
val += random_sample(arry, arry_size);
break;
}; // end switch
} // end inner_loop
} // end outer_loop
return 0;
}

View File

@ -0,0 +1,39 @@
// Test that misexpect detects mis-annotated switch statements
// RUN: llvm-profdata merge %S/Inputs/misexpect-switch.proftext -o %t.profdata
// RUN: %clang_cc1 %s -O2 -o - -emit-llvm -fprofile-instrument-use-path=%t.profdata -verify -Wmisexpect -debug-info-kind=line-tables-only
#define inner_loop 1000
#define outer_loop 20
#define arry_size 25
int arry[arry_size] = {0};
int rand();
int sum(int *buff, int size);
int random_sample(int *buff, int size);
int main() {
int val = 0;
int j, k;
for (j = 0; j < outer_loop; ++j) {
for (k = 0; k < inner_loop; ++k) {
unsigned condition = rand() % 10000;
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.}}
case 0:
val += sum(arry, arry_size);
break;
case 1:
case 2:
case 3:
break;
default:
val += random_sample(arry, arry_size);
break;
} // end switch
} // end inner_loop
} // end outer_loop
return 0;
}

73
llvm/docs/MisExpect.rst Normal file
View File

@ -0,0 +1,73 @@
===================
Misexpect
===================
.. contents::
.. toctree::
:maxdepth: 1
When developers use ``llvm.expect`` intrinsics, i.e., through use of
``__builtin_expect(...)``, they are trying to communicate how their code is
expected to behave at runtime to the optimizer. These annotations, however, can
be incorrect for a variety of reasons: changes to the code base invalidate them
silently, the developer mis-annotated them (e.g., using ``LIKELY`` instead of
``UNLIKELY``), or perhaps they assumed something incorrectly when they wrote
the annotation. Regardless of why, it is useful to detect these situations so
that the optimizer can make more useful decisions about the code. MisExpect
diagnostics are intended to help developers identify and address these
situations, by comparing the use of the ``llvm.expect`` intrinsic to the ground
truth provided by a profiling input.
The MisExpect checks in the LLVM backend follow a simple procedure: if there is
a mismatch between the branch weights collected during profiling and those
supplied by an ``llvm.expect`` intrinsic, then it will emit a diagnostic
message to the user.
The most natural place to perform the verification is just prior to when
branch weights are assigned to the target instruction in the form of
branch weight metadata.
There are 3 key places in the LLVM backend where branch weights are
created and assigned based on profiling information or the use of the
``llvm.expect`` intrinsic, and our implementation focuses on these
places to perform the verification.
We calculate the threshold for emitting MisExpect related diagnostics
based on the values the compiler assigns to ``llvm.expect`` intrinsics,
which can be set through the ``-likely-branch-weight`` and
``-unlikely-branch-weight`` LLVM options. During verification, if the
profile weights mismatch the calculated threshold, then we will emit a
remark or warning detailing a potential performance regression. The
diagnostic also reports the percentage of the time the annotation was
correct during profiling to help developers reason about how to proceed.
The diagnostics are also available in the form of optimization remarks,
which can be serialized and processed through the ``opt-viewer.py``
scripts in LLVM.
.. option:: -pass-remarks=misexpect
Enables optimization remarks for misexpect when profiling data conflicts with
use of ``llvm.expect`` intrinsics.
.. option:: -pgo-warn-misexpect
Enables misexpect warnings when profiling data conflicts with use of
``llvm.expect`` intrinsics.
LLVM supports 4 types of profile formats: Frontend, IR, CS-IR, and
Sampling. MisExpect Diagnostics are compatible with all Profiling formats.
+----------------+--------------------------------------------------------------------------------------+
| Profile Type | Description |
+================+======================================================================================+
| Frontend | Profiling instrumentation added during compilation by the frontend, i.e. ``clang`` |
+----------------+--------------------------------------------------------------------------------------+
| IR | Profiling instrumentation added during by the LLVM backend |
+----------------+--------------------------------------------------------------------------------------+
| CS-IR | Context Sensitive IR based profiles |
+----------------+--------------------------------------------------------------------------------------+
| Sampling | Profiles collected through sampling with external tools, such as ``perf`` on Linux |
+----------------+--------------------------------------------------------------------------------------+

View File

@ -85,6 +85,7 @@ enum DiagnosticKind {
DK_Unsupported,
DK_SrcMgr,
DK_DontCall,
DK_MisExpect,
DK_FirstPluginKind // Must be last value to work with
// getNextAvailablePluginDiagnosticKind
};
@ -1032,6 +1033,25 @@ public:
void print(DiagnosticPrinter &DP) const override;
};
/// Diagnostic information for MisExpect analysis.
class DiagnosticInfoMisExpect : public DiagnosticInfoWithLocationBase {
public:
DiagnosticInfoMisExpect(const Instruction *Inst, Twine &Msg);
/// \see DiagnosticInfo::print.
void print(DiagnosticPrinter &DP) const override;
static bool classof(const DiagnosticInfo *DI) {
return DI->getKind() == DK_MisExpect;
}
const Twine &getMsg() const { return Msg; }
private:
/// Message to report.
const Twine &Msg;
};
static DiagnosticSeverity getDiagnosticSeverity(SourceMgr::DiagKind DK) {
switch (DK) {
case llvm::SourceMgr::DK_Error:

View File

@ -202,6 +202,11 @@ public:
/// diagnostics.
void setDiagnosticsHotnessRequested(bool Requested);
bool getMisExpectWarningRequested() const;
void setMisExpectWarningRequested(bool Requested);
void setDiagnosticsMisExpectTolerance(Optional<uint64_t> Tolerance);
uint64_t getDiagnosticsMisExpectTolerance() const;
/// Return the minimum hotness value a diagnostic would need in order
/// to be included in optimization diagnostics.
///

View File

@ -144,6 +144,7 @@ namespace llvm {
ValueTrackingVariableLocations(false), ForceDwarfFrameSection(false),
XRayOmitFunctionIndex(false), DebugStrictDwarf(false),
Hotpatch(false), PPCGenScalarMASSEntries(false), JMCInstrument(false),
MisExpect(false),
FPDenormalMode(DenormalMode::IEEE, DenormalMode::IEEE) {}
/// DisableFramePointerElim - This returns true if frame pointer elimination
@ -356,6 +357,10 @@ namespace llvm {
/// Enable JustMyCode instrumentation.
unsigned JMCInstrument : 1;
/// When set to true, enable MisExpect Diagnostics
/// By default, it is set to false
unsigned MisExpect : 1;
/// Name of the stack usage file (i.e., .su file) if user passes
/// -fstack-usage. If empty, it can be implied that -fstack-usage is not
/// passed on the command line.

View File

@ -0,0 +1,77 @@
//===--- MisExpect.h - Check the use of llvm.expect with PGO data ---------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This contains code to emit diagnostic messages for potentially incorrect
// usage of the llvm.expect intrinsic. This utility extracts the threshold
// values from metadata associated with the instrumented Branch or Switch
// instruction. The threshold values are then used to determine if a diagnostic
// should be emitted.
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
namespace llvm {
namespace misexpect {
/// checkBackendInstrumentation - compares PGO counters to the thresholds used
/// for llvm.expect and warns if the PGO counters are outside of the expected
/// range. It extracts the expected weights from the MD_prof weights attatched
/// to the instruction, which are are assumed to come from lowered llvm.expect
/// intrinsics. The RealWeights parameter and the extracted expected weights are
/// then passed to verifyMisexpect() for verification
///
/// \param I The Instruction being checked
/// \param RealWeights A vector of profile weights for each target block
void checkBackendInstrumentation(Instruction &I,
const llvm::ArrayRef<uint32_t> RealWeights);
/// checkFrontendInstrumentation - compares PGO counters to the thresholds used
/// for llvm.expect and warns if the PGO counters are outside of the expected
/// range. It extracts the expected weights from the MD_prof weights attatched
/// to the instruction, which are are assumed to come from profiling data
/// attached by the frontend prior to llvm.expect intrinsic lowering. The
/// ExpectedWeights parameter and the extracted real weights are then passed to
/// verifyMisexpect() for verification
///
/// \param I The Instruction being checked
/// \param ExpectedWeights A vector of the expected weights for each target
/// block, this determines the threshold values used when emiting diagnostics
void checkFrontendInstrumentation(Instruction &I,
const ArrayRef<uint32_t> ExpectedWeights);
/// veryifyMisExpect - compares RealWeights to the thresholds used
/// for llvm.expect and warns if the PGO counters are outside of the expected
/// range.
///
/// \param I The Instruction being checked
/// \param RealWeights A vector of profile weights from the profile data
/// \param ExpectedWeights A vector of the weights attatch by llvm.expect
void verifyMisExpect(Instruction &I, ArrayRef<uint32_t> RealWeights,
const ArrayRef<uint32_t> ExpectedWeights);
/// checkExpectAnnotations - compares PGO counters to the thresholds used
/// for llvm.expect and warns if the PGO counters are outside of the expected
/// range. It extracts the expected weights from the MD_prof weights attatched
/// to the instruction, which are are assumed to come from lowered llvm.expect
/// intrinsics. The RealWeights parameter and the extracted expected weights are
/// then passed to verifyMisexpect() for verification. It is a thin wrapper
/// around the checkFrontendInstrumentation and checkBackendInstrumentation APIs
///
/// \param I The Instruction being checked
/// \param RealWeights A vector of profile weights for each target block
/// \param IsBackend A boolean describing if this is Frontend instrumentation
void checkExpectAnnotations(Instruction &I,
const ArrayRef<uint32_t> ExistingWeights,
bool IsFrontend);
} // namespace misexpect
} // namespace llvm

View File

@ -393,6 +393,17 @@ std::string DiagnosticInfoOptimizationBase::getMsg() const {
return OS.str();
}
DiagnosticInfoMisExpect::DiagnosticInfoMisExpect(const Instruction *Inst,
Twine &Msg)
: DiagnosticInfoWithLocationBase(DK_MisExpect, DS_Warning,
*Inst->getParent()->getParent(),
Inst->getDebugLoc()),
Msg(Msg) {}
void DiagnosticInfoMisExpect::print(DiagnosticPrinter &DP) const {
DP << getLocationStr() << ": " << getMsg();
}
void OptimizationRemarkAnalysisFPCommute::anchor() {}
void OptimizationRemarkAnalysisAliasing::anchor() {}

View File

@ -138,10 +138,22 @@ bool LLVMContext::getDiagnosticsHotnessRequested() const {
void LLVMContext::setDiagnosticsHotnessThreshold(Optional<uint64_t> Threshold) {
pImpl->DiagnosticsHotnessThreshold = Threshold;
}
void LLVMContext::setMisExpectWarningRequested(bool Requested) {
pImpl->MisExpectWarningRequested = Requested;
}
bool LLVMContext::getMisExpectWarningRequested() const {
return pImpl->MisExpectWarningRequested;
}
uint64_t LLVMContext::getDiagnosticsHotnessThreshold() const {
return pImpl->DiagnosticsHotnessThreshold.getValueOr(UINT64_MAX);
}
void LLVMContext::setDiagnosticsMisExpectTolerance(
Optional<uint64_t> Tolerance) {
pImpl->DiagnosticsMisExpectTolerance = Tolerance;
}
uint64_t LLVMContext::getDiagnosticsMisExpectTolerance() const {
return pImpl->DiagnosticsMisExpectTolerance.getValueOr(0);
}
bool LLVMContext::isDiagnosticsHotnessThresholdSetFromPSI() const {
return !pImpl->DiagnosticsHotnessThreshold.hasValue();

View File

@ -1380,6 +1380,11 @@ public:
/// If threshold option is not specified, it is disabled (0) by default.
Optional<uint64_t> DiagnosticsHotnessThreshold = 0;
/// The percentage of difference between profiling branch weights and
// llvm.expect branch weights to tolerate when emiting MisExpect diagnostics
Optional<uint64_t> DiagnosticsMisExpectTolerance = 0;
bool MisExpectWarningRequested = false;
/// The specialized remark streamer used by LLVM's OptimizationRemarkEmitter.
std::unique_ptr<LLVMRemarkStreamer> LLVMRS;

View File

@ -74,6 +74,8 @@
#include "llvm/Transforms/Instrumentation.h"
#include "llvm/Transforms/Utils/CallPromotionUtils.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/MisExpect.h"
#include "llvm/Transforms/Utils/SampleProfileInference.h"
#include "llvm/Transforms/Utils/SampleProfileLoaderBaseImpl.h"
#include "llvm/Transforms/Utils/SampleProfileLoaderBaseUtil.h"
#include <algorithm>
@ -1739,6 +1741,8 @@ void SampleProfileLoader::generateMDProfMetadata(Function &F) {
}
}
misexpect::checkExpectAnnotations(*TI, Weights, /*IsFrontend=*/false);
uint64_t TempWeight;
// Only set weights if there is at least one non-zero weight.
// In any other case, let the analyzer set weights.

View File

@ -110,6 +110,7 @@
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Instrumentation.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/MisExpect.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
#include <algorithm>
#include <cassert>
@ -2117,6 +2118,8 @@ void llvm::setProfMetadata(Module *M, Instruction *TI,
dbgs() << W << " ";
} dbgs() << "\n";);
misexpect::checkExpectAnnotations(*TI, Weights, /*IsFrontend=*/false);
TI->setMetadata(LLVMContext::MD_prof, MDB.createBranchWeights(Weights));
if (EmitBranchProbability) {
std::string BrCondStr = getBranchCondString(TI);

View File

@ -25,6 +25,7 @@
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils/MisExpect.h"
using namespace llvm;
@ -99,6 +100,8 @@ static bool handleSwitchExpect(SwitchInst &SI) {
uint64_t Index = (Case == *SI.case_default()) ? 0 : Case.getCaseIndex() + 1;
Weights[Index] = LikelyBranchWeightVal;
misexpect::checkExpectAnnotations(SI, Weights, /*IsFrontend=*/true);
SI.setCondition(ArgValue);
SI.setMetadata(LLVMContext::MD_prof,
@ -313,13 +316,16 @@ template <class BrSelInst> static bool handleBrSelExpect(BrSelInst &BSI) {
std::tie(LikelyBranchWeightVal, UnlikelyBranchWeightVal) =
getBranchWeight(Fn->getIntrinsicID(), CI, 2);
SmallVector<uint32_t, 4> ExpectedWeights;
if ((ExpectedValue->getZExtValue() == ValueComparedTo) ==
(Predicate == CmpInst::ICMP_EQ)) {
Node =
MDB.createBranchWeights(LikelyBranchWeightVal, UnlikelyBranchWeightVal);
ExpectedWeights = {LikelyBranchWeightVal, UnlikelyBranchWeightVal};
} else {
Node =
MDB.createBranchWeights(UnlikelyBranchWeightVal, LikelyBranchWeightVal);
ExpectedWeights = {UnlikelyBranchWeightVal, LikelyBranchWeightVal};
}
if (CmpI)
@ -327,6 +333,8 @@ template <class BrSelInst> static bool handleBrSelExpect(BrSelInst &BSI) {
else
BSI.setCondition(ArgValue);
misexpect::checkFrontendInstrumentation(BSI, ExpectedWeights);
BSI.setMetadata(LLVMContext::MD_prof, Node);
return true;
@ -407,7 +415,7 @@ public:
bool runOnFunction(Function &F) override { return lowerExpectIntrinsic(F); }
};
}
} // namespace
char LowerExpectIntrinsic::ID = 0;
INITIALIZE_PASS(LowerExpectIntrinsic, "lower-expect",

View File

@ -53,6 +53,7 @@ add_llvm_component_library(LLVMTransformUtils
MemoryTaggingSupport.cpp
Mem2Reg.cpp
MetaRenamer.cpp
MisExpect.cpp
ModuleUtils.cpp
NameAnonGlobals.cpp
PredicateInfo.cpp

View File

@ -0,0 +1,234 @@
//===--- MisExpect.cpp - Check the use of llvm.expect with PGO data -------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This contains code to emit warnings for potentially incorrect usage of the
// llvm.expect intrinsic. This utility extracts the threshold values from
// metadata associated with the instrumented Branch or Switch instruction. The
// threshold values are then used to determine if a warning should be emmited.
//
// MisExpect's implementation relies on two assumptions about how branch weights
// are managed in LLVM.
//
// 1) Frontend profiling weights are always in place before llvm.expect is
// lowered in LowerExpectIntrinsic.cpp. Frontend based instrumentation therefore
// needs to extract the branch weights and then compare them to the weights
// being added by the llvm.expect intrinsic lowering.
//
// 2) Sampling and IR based profiles will *only* have branch weight metadata
// before profiling data is consulted if they are from a lowered llvm.expect
// intrinsic. These profiles thus always extract the expected weights and then
// compare them to the weights collected during profiling to determine if a
// diagnostic message is warranted.
//
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Utils/MisExpect.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Support/BranchProbability.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FormatVariadic.h"
#include <cstdint>
#include <functional>
#include <numeric>
#define DEBUG_TYPE "misexpect"
using namespace llvm;
using namespace misexpect;
namespace llvm {
// Command line option to enable/disable the warning when profile data suggests
// a mismatch with the use of the llvm.expect intrinsic
static cl::opt<bool> PGOWarnMisExpect(
"pgo-warn-misexpect", cl::init(false), cl::Hidden,
cl::desc("Use this option to turn on/off "
"warnings about incorrect usage of llvm.expect intrinsics."));
static cl::opt<unsigned> MisExpectTolerance(
"misexpect-tolerance", cl::init(0),
cl::desc("Prevents emiting diagnostics when profile counts are "
"within N% of the threshold.."));
} // namespace llvm
namespace {
bool isMisExpectDiagEnabled(LLVMContext &Ctx) {
return PGOWarnMisExpect || Ctx.getMisExpectWarningRequested();
}
uint64_t getMisExpectTolerance(LLVMContext &Ctx) {
return std::max(static_cast<uint64_t>(MisExpectTolerance),
Ctx.getDiagnosticsMisExpectTolerance());
}
Instruction *getInstCondition(Instruction *I) {
assert(I != nullptr && "MisExpect target Instruction cannot be nullptr");
Instruction *Ret = nullptr;
if (auto *B = dyn_cast<BranchInst>(I)) {
Ret = dyn_cast<Instruction>(B->getCondition());
}
// TODO: Find a way to resolve condition location for switches
// Using the condition of the switch seems to often resolve to an earlier
// point in the program, i.e. the calculation of the switch condition, rather
// than the switch's location in the source code. Thus, we should use the
// instruction to get source code locations rather than the condition to
// improve diagnostic output, such as the caret. If the same problem exists
// for branch instructions, then we should remove this function and directly
// use the instruction
//
else if (auto *S = dyn_cast<SwitchInst>(I)) {
Ret = dyn_cast<Instruction>(S->getCondition());
}
return Ret ? Ret : I;
}
void emitMisexpectDiagnostic(Instruction *I, LLVMContext &Ctx,
uint64_t ProfCount, uint64_t TotalCount) {
double PercentageCorrect = (double)ProfCount / TotalCount;
auto PerString =
formatv("{0:P} ({1} / {2})", PercentageCorrect, ProfCount, TotalCount);
auto RemStr = formatv(
"Potential performance regression from use of the llvm.expect intrinsic: "
"Annotation was correct on {0} of profiled executions.",
PerString);
Twine Msg(PerString);
Instruction *Cond = getInstCondition(I);
if (isMisExpectDiagEnabled(Ctx))
Ctx.diagnose(DiagnosticInfoMisExpect(Cond, Msg));
OptimizationRemarkEmitter ORE(I->getParent()->getParent());
ORE.emit(OptimizationRemark(DEBUG_TYPE, "misexpect", Cond) << RemStr.str());
}
} // namespace
namespace llvm {
namespace misexpect {
// Helper function to extract branch weights into a vector
Optional<SmallVector<uint32_t, 4>> extractWeights(Instruction *I,
LLVMContext &Ctx) {
assert(I && "MisExpect::extractWeights given invalid pointer");
auto *ProfileData = I->getMetadata(LLVMContext::MD_prof);
if (!ProfileData)
return None;
unsigned NOps = ProfileData->getNumOperands();
if (NOps < 3)
return None;
auto *ProfDataName = dyn_cast<MDString>(ProfileData->getOperand(0));
if (!ProfDataName || !ProfDataName->getString().equals("branch_weights"))
return None;
SmallVector<uint32_t, 4> Weights(NOps - 1);
for (unsigned Idx = 1; Idx < NOps; Idx++) {
ConstantInt *Value =
mdconst::dyn_extract<ConstantInt>(ProfileData->getOperand(Idx));
uint32_t V = Value->getZExtValue();
Weights[Idx - 1] = V;
}
return Weights;
}
void verifyMisExpect(Instruction &I, ArrayRef<uint32_t> RealWeights,
ArrayRef<uint32_t> ExpectedWeights) {
// To determine if we emit a diagnostic, we need to compare the branch weights
// from the profile to those added by the llvm.expect intrinsic.
// So first, we extract the "likely" and "unlikely" weights from
// ExpectedWeights And determine the correct weight in the profile to compare
// against.
uint64_t LikelyBranchWeight = 0,
UnlikelyBranchWeight = std::numeric_limits<uint32_t>::max();
size_t MaxIndex = 0;
for (size_t Idx = 0, End = ExpectedWeights.size(); Idx < End; Idx++) {
uint32_t V = ExpectedWeights[Idx];
if (LikelyBranchWeight < V) {
LikelyBranchWeight = V;
MaxIndex = Idx;
}
if (UnlikelyBranchWeight > V) {
UnlikelyBranchWeight = V;
}
}
const uint64_t ProfiledWeight = RealWeights[MaxIndex];
const uint64_t RealWeightsTotal =
std::accumulate(RealWeights.begin(), RealWeights.end(), (uint64_t)0,
std::plus<uint64_t>());
const uint64_t NumUnlikelyTargets = RealWeights.size() - 1;
const uint64_t TotalBranchWeight =
LikelyBranchWeight + (UnlikelyBranchWeight * NumUnlikelyTargets);
// To determine our threshold value we need to obtain the branch probability
// for the weights added by llvm.expect and use that proportion to calculate
// our threshold based on the collected profile data.
const llvm::BranchProbability LikelyProbablilty(LikelyBranchWeight,
TotalBranchWeight);
uint64_t ScaledThreshold = LikelyProbablilty.scale(RealWeightsTotal);
// clamp tolerance range to [0, 100)
auto Tolerance = getMisExpectTolerance(I.getContext());
if (Tolerance < 0)
Tolerance = 0;
else if (MisExpectTolerance > 99)
Tolerance = 99;
// Allow users to relax checking by N% i.e., if they use a 5% tolerance,
// then we check against 0.95*ScaledThreshold
if (Tolerance > 0)
ScaledThreshold *= (1.0 - Tolerance / 100.0);
// When the profile weight is below the threshold, we emit the diagnostic
if (ProfiledWeight < ScaledThreshold)
emitMisexpectDiagnostic(&I, I.getContext(), ProfiledWeight,
RealWeightsTotal);
}
void checkBackendInstrumentation(Instruction &I,
const ArrayRef<uint32_t> RealWeights) {
auto ExpectedWeightsOpt = extractWeights(&I, I.getContext());
if (!ExpectedWeightsOpt.hasValue())
return;
auto ExpectedWeights = ExpectedWeightsOpt.getValue();
verifyMisExpect(I, RealWeights, ExpectedWeights);
}
void checkFrontendInstrumentation(Instruction &I,
const ArrayRef<uint32_t> ExpectedWeights) {
auto RealWeightsOpt = extractWeights(&I, I.getContext());
if (!RealWeightsOpt.hasValue())
return;
auto RealWeights = RealWeightsOpt.getValue();
verifyMisExpect(I, RealWeights, ExpectedWeights);
}
void checkExpectAnnotations(Instruction &I,
const ArrayRef<uint32_t> ExistingWeights,
bool IsFrontendInstr) {
if (IsFrontendInstr) {
checkFrontendInstrumentation(I, ExistingWeights);
} else {
checkBackendInstrumentation(I, ExistingWeights);
}
}
} // namespace misexpect
} // namespace llvm
#undef DEBUG_TYPE

View File

@ -0,0 +1,38 @@
# IR level Instrumentation Flag
:ir
bar
# Func Hash:
146835647075900052
# Num Counters:
2
# Counter Values:
200000
0
baz
# Func Hash:
12884901887
# Num Counters:
1
# Counter Values:
399668
foo
# Func Hash:
29212902728
# Num Counters:
2
# Counter Values:
40803991
1600332
main
# Func Hash:
41605652536
# Num Counters:
3
# Counter Values:
2000000
2000
1

View File

@ -0,0 +1,38 @@
# IR level Instrumentation Flag
:ir
bar
# Func Hash:
146835647075900052
# Num Counters:
2
# Counter Values:
100
10
baz
# Func Hash:
12884901887
# Num Counters:
1
# Counter Values:
399668
foo
# Func Hash:
29212902728
# Num Counters:
2
# Counter Values:
40803991
1600332
main
# Func Hash:
41605652536
# Num Counters:
3
# Counter Values:
2000000
2000
1

View File

@ -0,0 +1,38 @@
# IR level Instrumentation Flag
:ir
bar
# Func Hash:
146835647075900052
# Num Counters:
2
# Counter Values:
399668
1600332
baz
# Func Hash:
12884901887
# Num Counters:
1
# Counter Values:
399668
foo
# Func Hash:
29212902728
# Num Counters:
2
# Counter Values:
40803991
1600332
main
# Func Hash:
41605652536
# Num Counters:
3
# Counter Values:
2000000
2000
1

View File

@ -0,0 +1,15 @@
# IR level Instrumentation Flag
:ir
main
# Func Hash:
391331300939170156
# Num Counters:
7
# Counter Values:
0
0
20000
0
0
1
0

View File

@ -0,0 +1,15 @@
# IR level Instrumentation Flag
:ir
main
# Func Hash:
391331300939170156
# Num Counters:
7
# Counter Values:
3973
3970
0
11889
8111
1
0

View File

@ -0,0 +1,94 @@
; Test misexpect checks do not issue diagnostics when profiling weights and
; branch weights added by llvm.expect agree
; RUN: llvm-profdata merge %S/Inputs/misexpect-branch-correct.proftext -o %t.profdata
; 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-NOT: warning: {{.*}}
; CHECK-NOT: remark: {{.*}}
; CHECK: !{!"branch_weights", i32 0, i32 200000}
; ModuleID = 'misexpect-branch-correct.c'
source_filename = "misexpect-branch-correct.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@inner_loop = constant i32 100, align 4
@outer_loop = constant i32 2000, align 4
; Function Attrs: nounwind
define i32 @bar() #0 {
entry:
%rando = alloca i32, align 4
%x = alloca i32, align 4
%0 = bitcast i32* %rando to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #4
%call = call i32 (...) @buzz()
store i32 %call, i32* %rando, align 4, !tbaa !3
%1 = bitcast i32* %x to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* %1) #4
store i32 0, i32* %x, align 4, !tbaa !3
%2 = load i32, i32* %rando, align 4, !tbaa !3
%rem = srem i32 %2, 200000
%cmp = icmp eq i32 %rem, 0
%lnot = xor i1 %cmp, true
%lnot1 = xor i1 %lnot, true
%lnot.ext = zext i1 %lnot1 to i32
%conv = sext i32 %lnot.ext to i64
%expval = call i64 @llvm.expect.i64(i64 %conv, i64 0)
%tobool = icmp ne i64 %expval, 0
br i1 %tobool, label %if.then, label %if.else
if.then: ; preds = %entry
%3 = load i32, i32* %rando, align 4, !tbaa !3
%call2 = call i32 @baz(i32 %3)
store i32 %call2, i32* %x, align 4, !tbaa !3
br label %if.end
if.else: ; preds = %entry
%call3 = call i32 @foo(i32 50)
store i32 %call3, i32* %x, align 4, !tbaa !3
br label %if.end
if.end: ; preds = %if.else, %if.then
%4 = load i32, i32* %x, align 4, !tbaa !3
%5 = bitcast i32* %x to i8*
call void @llvm.lifetime.end.p0i8(i64 4, i8* %5) #4
%6 = bitcast i32* %rando to i8*
call void @llvm.lifetime.end.p0i8(i64 4, i8* %6) #4
ret i32 %4
}
; Function Attrs: argmemonly nounwind willreturn
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
declare i32 @buzz(...) #2
; Function Attrs: nounwind readnone willreturn
declare i64 @llvm.expect.i64(i64, i64) #3
declare i32 @baz(i32) #2
declare i32 @foo(i32) #2
; Function Attrs: argmemonly nounwind willreturn
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
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" }
attributes #1 = { argmemonly nounwind willreturn }
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" }
attributes #3 = { nounwind readnone willreturn }
attributes #4 = { nounwind }
!llvm.module.flags = !{!0, !1}
!llvm.ident = !{!2}
!0 = !{i32 2, !"Debug Info Version", i32 3}
!1 = !{i32 1, !"wchar_size", i32 4}
!2 = !{!"clang version 10.0.0 (c20270bfffc9d6965219de339d66c61e9fe7d82d)"}
!3 = !{!4, !4, i64 0}
!4 = !{!"int", !5, i64 0}
!5 = !{!"omnipotent char", !6, i64 0}
!6 = !{!"Simple C/C++ TBAA"}

View File

@ -0,0 +1,104 @@
; Test that misexpect diagnostics handle stripped debug info gracefully
; RUN: llvm-profdata merge %S/Inputs/misexpect-branch.proftext -o %t.profdata
; 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
; 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
; 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
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -S 2>&1 | FileCheck %s --check-prefix=DISABLED
; WARNING-DAG: warning: <unknown>:0:0: 19.98%
; 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.
; REMARK-NOT: warning: <unknown>:0:0: 19.98%
; 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.
; BOTH-DAG: warning: <unknown>:0:0: 19.98%
; 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.
; DISABLED-NOT: warning: <unknown>:0:0: 19.98%
; 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.
; ModuleID = 'misexpect-branch.c'
source_filename = "misexpect-branch.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@inner_loop = constant i32 100, align 4
@outer_loop = constant i32 2000, align 4
; Function Attrs: nounwind
define i32 @bar() #0 {
entry:
%rando = alloca i32, align 4
%x = alloca i32, align 4
%0 = bitcast i32* %rando to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #4
%call = call i32 (...) @buzz()
store i32 %call, i32* %rando, align 4, !tbaa !3
%1 = bitcast i32* %x to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* %1) #4
store i32 0, i32* %x, align 4, !tbaa !3
%2 = load i32, i32* %rando, align 4, !tbaa !3
%rem = srem i32 %2, 200000
%cmp = icmp eq i32 %rem, 0
%lnot = xor i1 %cmp, true
%lnot1 = xor i1 %lnot, true
%lnot.ext = zext i1 %lnot1 to i32
%conv = sext i32 %lnot.ext to i64
%expval = call i64 @llvm.expect.i64(i64 %conv, i64 1)
%tobool = icmp ne i64 %expval, 0
br i1 %tobool, label %if.then, label %if.else
if.then: ; preds = %entry
%3 = load i32, i32* %rando, align 4, !tbaa !3
%call2 = call i32 @baz(i32 %3)
store i32 %call2, i32* %x, align 4, !tbaa !3
br label %if.end
if.else: ; preds = %entry
%call3 = call i32 @foo(i32 50)
store i32 %call3, i32* %x, align 4, !tbaa !3
br label %if.end
if.end: ; preds = %if.else, %if.then
%4 = load i32, i32* %x, align 4, !tbaa !3
%5 = bitcast i32* %x to i8*
call void @llvm.lifetime.end.p0i8(i64 4, i8* %5) #4
%6 = bitcast i32* %rando to i8*
call void @llvm.lifetime.end.p0i8(i64 4, i8* %6) #4
ret i32 %4
}
; Function Attrs: argmemonly nounwind willreturn
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
declare i32 @buzz(...) #2
; Function Attrs: nounwind readnone willreturn
declare i64 @llvm.expect.i64(i64, i64) #3
declare i32 @baz(i32) #2
declare i32 @foo(i32) #2
; Function Attrs: argmemonly nounwind willreturn
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
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" }
attributes #1 = { argmemonly nounwind willreturn }
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" }
attributes #3 = { nounwind readnone willreturn }
attributes #4 = { nounwind }
!llvm.module.flags = !{!0, !1}
!llvm.ident = !{!2}
!0 = !{i32 2, !"Debug Info Version", i32 3}
!1 = !{i32 1, !"wchar_size", i32 4}
!2 = !{!"clang version 10.0.0 (trunk c20270bfffc9d6965219de339d66c61e9fe7d82d)"}
!3 = !{!4, !4, i64 0}
!4 = !{!"int", !5, i64 0}
!5 = !{!"omnipotent char", !6, i64 0}
!6 = !{!"Simple C/C++ TBAA"}

View File

@ -0,0 +1,87 @@
; Test misexpect doesn't issue diagnostics when a branch is marked unpredictable
; RUN: llvm-profdata merge %S/Inputs/misexpect-branch-correct.proftext -o %t.profdata
; 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-NOT: warning: {{.*}}
; CHECK-NOT: remark: {{.*}}
; ModuleID = 'misexpect-branch-unpredictable.c'
source_filename = "clang/test/Profile/misexpect-branch-unpredictable.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@inner_loop = constant i32 100, align 4
@outer_loop = constant i32 2000, align 4
; Function Attrs: nounwind
define i32 @bar() #0 {
entry:
%rando = alloca i32, align 4
%x = alloca i32, align 4
%0 = bitcast i32* %rando to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #3
%call = call i32 (...) @buzz()
store i32 %call, i32* %rando, align 4, !tbaa !2
%1 = bitcast i32* %x to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* %1) #3
store i32 0, i32* %x, align 4, !tbaa !2
%2 = load i32, i32* %rando, align 4, !tbaa !2
%rem = srem i32 %2, 200000
%cmp = icmp eq i32 %rem, 0
%lnot = xor i1 %cmp, true
%lnot1 = xor i1 %lnot, true
%lnot.ext = zext i1 %lnot1 to i32
%conv = sext i32 %lnot.ext to i64
%tobool = icmp ne i64 %conv, 0
br i1 %tobool, label %if.then, label %if.else, !unpredictable !6
if.then: ; preds = %entry
%3 = load i32, i32* %rando, align 4, !tbaa !2
%call2 = call i32 @baz(i32 %3)
store i32 %call2, i32* %x, align 4, !tbaa !2
br label %if.end
if.else: ; preds = %entry
%call3 = call i32 @foo(i32 50)
store i32 %call3, i32* %x, align 4, !tbaa !2
br label %if.end
if.end: ; preds = %if.else, %if.then
%4 = load i32, i32* %x, align 4, !tbaa !2
%5 = bitcast i32* %x to i8*
call void @llvm.lifetime.end.p0i8(i64 4, i8* %5) #3
%6 = bitcast i32* %rando to i8*
call void @llvm.lifetime.end.p0i8(i64 4, i8* %6) #3
ret i32 %4
}
; Function Attrs: argmemonly nounwind willreturn
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
declare i32 @buzz(...) #2
declare i32 @baz(i32) #2
declare i32 @foo(i32) #2
; Function Attrs: argmemonly nounwind willreturn
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
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" }
attributes #1 = { argmemonly nounwind willreturn }
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" }
attributes #3 = { nounwind }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"Fuchsia clang version 10.0.0 (153b453014c94291c8c6cf6320b2f46df40f26f3) (based on LLVM 10.0.0svn)"}
!2 = !{!3, !3, i64 0}
!3 = !{!"int", !4, i64 0}
!4 = !{!"omnipotent char", !5, i64 0}
!5 = !{!"Simple C/C++ TBAA"}
!6 = !{}

View File

@ -0,0 +1,124 @@
; Test that misexpect diagnostics are emtted when profile branch weights are
; below the branch weights added by llvm.expect intrinsics
; RUN: llvm-profdata merge %S/Inputs/misexpect-branch.proftext -o %t.profdata
; 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
; 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
; 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
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -S 2>&1 | FileCheck %s --check-prefix=DISABLED
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -pgo-warn-misexpect -misexpect-tolerance=81 -pass-remarks=misexpect -S 2>&1 | FileCheck %s --check-prefix=THRESHOLD
; WARNING-DAG: warning: misexpect-branch.c:22:0: 19.98%
; 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.
; REMARK-NOT: warning: misexpect-branch.c:22:0: 19.98%
; 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.
; BOTH-DAG: warning: misexpect-branch.c:22:0: 19.98%
; 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.
; DISABLED-NOT: warning: misexpect-branch.c:22:0: 19.98%
; 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.
; THRESHOLD-NOT: warning: misexpect-branch.c:22:0: 19.98%
; THRESHOLD-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.
; ModuleID = 'misexpect-branch.c'
source_filename = "misexpect-branch.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@inner_loop = constant i32 100, align 4
@outer_loop = constant i32 2000, align 4
; Function Attrs: nounwind
define i32 @bar() #0 !dbg !6 {
entry:
%rando = alloca i32, align 4
%x = alloca i32, align 4
%0 = bitcast i32* %rando to i8*, !dbg !9
call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #4, !dbg !9
%call = call i32 (...) @buzz(), !dbg !9
store i32 %call, i32* %rando, align 4, !dbg !9, !tbaa !10
%1 = bitcast i32* %x to i8*, !dbg !14
call void @llvm.lifetime.start.p0i8(i64 4, i8* %1) #4, !dbg !14
store i32 0, i32* %x, align 4, !dbg !14, !tbaa !10
%2 = load i32, i32* %rando, align 4, !dbg !15, !tbaa !10
%rem = srem i32 %2, 200000, !dbg !15
%cmp = icmp eq i32 %rem, 0, !dbg !15
%lnot = xor i1 %cmp, true, !dbg !15
%lnot1 = xor i1 %lnot, true, !dbg !15
%lnot.ext = zext i1 %lnot1 to i32, !dbg !15
%conv = sext i32 %lnot.ext to i64, !dbg !15
%expval = call i64 @llvm.expect.i64(i64 %conv, i64 1), !dbg !15
%tobool = icmp ne i64 %expval, 0, !dbg !15
br i1 %tobool, label %if.then, label %if.else, !dbg !15
if.then: ; preds = %entry
%3 = load i32, i32* %rando, align 4, !dbg !16, !tbaa !10
%call2 = call i32 @baz(i32 %3), !dbg !16
store i32 %call2, i32* %x, align 4, !dbg !16, !tbaa !10
br label %if.end, !dbg !17
if.else: ; preds = %entry
%call3 = call i32 @foo(i32 50), !dbg !18
store i32 %call3, i32* %x, align 4, !dbg !18, !tbaa !10
br label %if.end
if.end: ; preds = %if.else, %if.then
%4 = load i32, i32* %x, align 4, !dbg !19, !tbaa !10
%5 = bitcast i32* %x to i8*, !dbg !20
call void @llvm.lifetime.end.p0i8(i64 4, i8* %5) #4, !dbg !20
%6 = bitcast i32* %rando to i8*, !dbg !20
call void @llvm.lifetime.end.p0i8(i64 4, i8* %6) #4, !dbg !20
ret i32 %4, !dbg !19
}
; Function Attrs: argmemonly nounwind willreturn
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
declare i32 @buzz(...) #2
; Function Attrs: nounwind readnone willreturn
declare i64 @llvm.expect.i64(i64, i64) #3
declare i32 @baz(i32) #2
declare i32 @foo(i32) #2
; Function Attrs: argmemonly nounwind willreturn
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
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" }
attributes #1 = { argmemonly nounwind willreturn }
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" }
attributes #3 = { nounwind readnone willreturn }
attributes #4 = { nounwind }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4}
!llvm.ident = !{!5}
!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)
!1 = !DIFile(filename: "<stdin>", directory: ".")
!2 = !{}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = !{i32 1, !"wchar_size", i32 4}
!5 = !{!"clang version 10.0.0 (trunk c20270bfffc9d6965219de339d66c61e9fe7d82d)"}
!6 = distinct !DISubprogram(name: "bar", scope: !7, file: !7, line: 19, type: !8, scopeLine: 19, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
!7 = !DIFile(filename: "misexpect-branch.c", directory: ".")
!8 = !DISubroutineType(types: !2)
!9 = !DILocation(line: 20, scope: !6)
!10 = !{!11, !11, i64 0}
!11 = !{!"int", !12, i64 0}
!12 = !{!"omnipotent char", !13, i64 0}
!13 = !{!"Simple C/C++ TBAA"}
!14 = !DILocation(line: 21, scope: !6)
!15 = !DILocation(line: 22, scope: !6)
!16 = !DILocation(line: 23, scope: !6)
!17 = !DILocation(line: 24, scope: !6)
!18 = !DILocation(line: 25, scope: !6)
!19 = !DILocation(line: 27, scope: !6)
!20 = !DILocation(line: 28, scope: !6)

View File

@ -0,0 +1,189 @@
; Test misexpect handles switch statements when debug information is stripped
; RUN: llvm-profdata merge %S/Inputs/misexpect-switch.proftext -o %t.profdata
; 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
; 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
; 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
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -S 2>&1 | FileCheck %s --check-prefix=DISABLED
; WARNING-DAG: warning: <unknown>:0:0: 0.00%
; 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.
; REMARK-NOT: warning: <unknown>:0:0: 0.00%
; 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.
; BOTH-DAG: warning: <unknown>:0:0: 0.00%
; 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.
; DISABLED-NOT: warning: <unknown>:0:0: 0.00%
; 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.
; DISABLED-NOT: warning: <unknown>:0:0: 0.00%
; 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.
; CORRECT-NOT: warning: {{.*}}
; CORRECT-NOT: remark: {{.*}}
; ModuleID = 'misexpect-switch.c'
source_filename = "misexpect-switch.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@inner_loop = dso_local constant i32 1000, align 4
@outer_loop = dso_local constant i32 20, align 4
@arry_size = dso_local constant i32 25, align 4
@arry = dso_local global [25 x i32] zeroinitializer, align 16
; Function Attrs: nounwind uwtable
define dso_local void @init_arry() #0 {
entry:
%i = alloca i32, align 4
%0 = bitcast i32* %i to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #6
store i32 0, i32* %i, align 4, !tbaa !4
br label %for.cond
for.cond: ; preds = %for.inc, %entry
%1 = load i32, i32* %i, align 4, !tbaa !4
%cmp = icmp slt i32 %1, 25
br i1 %cmp, label %for.body, label %for.end
for.body: ; preds = %for.cond
%call = call i32 @rand() #6
%rem = srem i32 %call, 10
%2 = load i32, i32* %i, align 4, !tbaa !4
%idxprom = sext i32 %2 to i64
%arrayidx = getelementptr inbounds [25 x i32], [25 x i32]* @arry, i64 0, i64 %idxprom
store i32 %rem, i32* %arrayidx, align 4, !tbaa !4
br label %for.inc
for.inc: ; preds = %for.body
%3 = load i32, i32* %i, align 4, !tbaa !4
%inc = add nsw i32 %3, 1
store i32 %inc, i32* %i, align 4, !tbaa !4
br label %for.cond
for.end: ; preds = %for.cond
%4 = bitcast i32* %i to i8*
call void @llvm.lifetime.end.p0i8(i64 4, i8* %4) #6
ret void
}
; Function Attrs: argmemonly nounwind willreturn
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
; Function Attrs: nounwind readnone speculatable willreturn
declare void @llvm.dbg.declare(metadata, metadata, metadata) #2
; Function Attrs: nounwind
declare dso_local i32 @rand() #3
; Function Attrs: argmemonly nounwind willreturn
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
; Function Attrs: nounwind uwtable
define dso_local i32 @main() #0 {
entry:
%retval = alloca i32, align 4
%val = alloca i32, align 4
%j = alloca i32, align 4
%condition = alloca i32, align 4
store i32 0, i32* %retval, align 4
call void @init_arry()
%0 = bitcast i32* %val to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #6
store i32 0, i32* %val, align 4, !tbaa !4
%1 = bitcast i32* %j to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* %1) #6
store i32 0, i32* %j, align 4, !tbaa !4
br label %for.cond
for.cond: ; preds = %for.inc, %entry
%2 = load i32, i32* %j, align 4, !tbaa !4
%cmp = icmp slt i32 %2, 20000
br i1 %cmp, label %for.body, label %for.end
for.body: ; preds = %for.cond
%3 = bitcast i32* %condition to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* %3) #6
%call = call i32 @rand() #6
%rem = srem i32 %call, 5
store i32 %rem, i32* %condition, align 4, !tbaa !4
%4 = load i32, i32* %condition, align 4, !tbaa !4
%conv = zext i32 %4 to i64
%expval = call i64 @llvm.expect.i64(i64 %conv, i64 6)
switch i64 %expval, label %sw.default [
i64 0, label %sw.bb
i64 1, label %sw.bb2
i64 2, label %sw.bb2
i64 3, label %sw.bb2
i64 4, label %sw.bb3
]
sw.bb: ; preds = %for.body
%call1 = call i32 @sum(i32* getelementptr inbounds ([25 x i32], [25 x i32]* @arry, i64 0, i64 0), i32 25)
%5 = load i32, i32* %val, align 4, !tbaa !4
%add = add nsw i32 %5, %call1
store i32 %add, i32* %val, align 4, !tbaa !4
br label %sw.epilog
sw.bb2: ; preds = %for.body, %for.body, %for.body
br label %sw.epilog
sw.bb3: ; preds = %for.body
%call4 = call i32 @random_sample(i32* getelementptr inbounds ([25 x i32], [25 x i32]* @arry, i64 0, i64 0), i32 25)
%6 = load i32, i32* %val, align 4, !tbaa !4
%add5 = add nsw i32 %6, %call4
store i32 %add5, i32* %val, align 4, !tbaa !4
br label %sw.epilog
sw.default: ; preds = %for.body
unreachable
sw.epilog: ; preds = %sw.bb3, %sw.bb2, %sw.bb
%7 = bitcast i32* %condition to i8*
call void @llvm.lifetime.end.p0i8(i64 4, i8* %7) #6
br label %for.inc
for.inc: ; preds = %sw.epilog
%8 = load i32, i32* %j, align 4, !tbaa !4
%inc = add nsw i32 %8, 1
store i32 %inc, i32* %j, align 4, !tbaa !4
br label %for.cond
for.end: ; preds = %for.cond
%9 = bitcast i32* %j to i8*
call void @llvm.lifetime.end.p0i8(i64 4, i8* %9) #6
%10 = bitcast i32* %val to i8*
call void @llvm.lifetime.end.p0i8(i64 4, i8* %10) #6
ret i32 0
}
; Function Attrs: nounwind readnone willreturn
declare i64 @llvm.expect.i64(i64, i64) #4
declare dso_local i32 @sum(i32*, i32) #5
declare dso_local i32 @random_sample(i32*, i32) #5
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" }
attributes #1 = { argmemonly nounwind willreturn }
attributes #2 = { nounwind readnone speculatable willreturn }
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" }
attributes #4 = { nounwind readnone willreturn }
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" }
attributes #6 = { nounwind }
!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}
!0 = !{i32 2, !"Dwarf Version", i32 4}
!1 = !{i32 2, !"Debug Info Version", i32 3}
!2 = !{i32 1, !"wchar_size", i32 4}
!3 = !{!"clang version 10.0.0 (60b79b85b1763d3d25630261e5cd1adb7f0835bc)"}
!4 = !{!5, !5, i64 0}
!5 = !{!"int", !6, i64 0}
!6 = !{!"omnipotent char", !7, i64 0}
!7 = !{!"Simple C/C++ TBAA"}

View File

@ -0,0 +1,285 @@
; Test misexpect diagnostics handle swich statements, and report source locations correctly
; RUN: llvm-profdata merge %S/Inputs/misexpect-switch.proftext -o %t.profdata
; RUN: llvm-profdata merge %S/Inputs/misexpect-switch-correct.proftext -o %t.c.profdata
; 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
; 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
; 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
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.profdata -S 2>&1 | FileCheck %s --check-prefix=DISABLED
; RUN: opt < %s -passes="function(lower-expect),pgo-instr-use" -pgo-test-profile-file=%t.c.profdata -pgo-warn-misexpect -pass-remarks=misexpect -S 2>&1 | FileCheck %s --check-prefix=CORRECT
; WARNING-DAG: warning: misexpect-switch.c:26:30: 0.00%
; WARNING-NOT: remark: misexpect-switch.c:26:30: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 0.00% (0 / 8112) of profiled executions.
; REMARK-NOT: warning: misexpect-switch.c:26:30: 0.00%
; REMARK-DAG: remark: misexpect-switch.c:26:30: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 0.00% (0 / 8112) of profiled executions.
; BOTH-DAG: warning: misexpect-switch.c:26:30: 0.00%
; BOTH-DAG: remark: misexpect-switch.c:26:30: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 0.00% (0 / 8112) of profiled executions.
; DISABLED-NOT: warning: misexpect-switch.c:26:30: 0.00%
; DISABLED-NOT: remark: misexpect-switch.c:26:30: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 0.00% (0 / 8112) of profiled executions.
; DISABLED-NOT: warning: misexpect-switch.c:26:30: 0.00%
; DISABLED-NOT: remark: misexpect-switch.c:26:30: Potential performance regression from use of the llvm.expect intrinsic: Annotation was correct on 0.00% (0 / 8112) of profiled executions.
; CORRECT-NOT: warning: {{.*}}
; CORRECT-NOT: remark: {{.*}}
; ModuleID = 'misexpect-switch.c'
source_filename = "misexpect-switch.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@inner_loop = dso_local constant i32 1000, align 4, !dbg !0
@outer_loop = dso_local constant i32 20, align 4, !dbg !6
@arry_size = dso_local constant i32 25, align 4, !dbg !10
@arry = dso_local global [25 x i32] zeroinitializer, align 16, !dbg !12
; Function Attrs: nounwind uwtable
define dso_local void @init_arry() #0 !dbg !21 {
entry:
%i = alloca i32, align 4
%0 = bitcast i32* %i to i8*, !dbg !26
call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #6, !dbg !26
call void @llvm.dbg.declare(metadata i32* %i, metadata !25, metadata !DIExpression()), !dbg !27
store i32 0, i32* %i, align 4, !dbg !28, !tbaa !30
br label %for.cond, !dbg !34
for.cond: ; preds = %for.inc, %entry
%1 = load i32, i32* %i, align 4, !dbg !35, !tbaa !30
%cmp = icmp slt i32 %1, 25, !dbg !37
br i1 %cmp, label %for.body, label %for.end, !dbg !38
for.body: ; preds = %for.cond
%call = call i32 @rand() #6, !dbg !39
%rem = srem i32 %call, 10, !dbg !41
%2 = load i32, i32* %i, align 4, !dbg !42, !tbaa !30
%idxprom = sext i32 %2 to i64, !dbg !43
%arrayidx = getelementptr inbounds [25 x i32], [25 x i32]* @arry, i64 0, i64 %idxprom, !dbg !43
store i32 %rem, i32* %arrayidx, align 4, !dbg !44, !tbaa !30
br label %for.inc, !dbg !45
for.inc: ; preds = %for.body
%3 = load i32, i32* %i, align 4, !dbg !46, !tbaa !30
%inc = add nsw i32 %3, 1, !dbg !46
store i32 %inc, i32* %i, align 4, !dbg !46, !tbaa !30
br label %for.cond, !dbg !47, !llvm.loop !48
for.end: ; preds = %for.cond
%4 = bitcast i32* %i to i8*, !dbg !50
call void @llvm.lifetime.end.p0i8(i64 4, i8* %4) #6, !dbg !50
ret void, !dbg !50
}
; Function Attrs: argmemonly nounwind willreturn
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
; Function Attrs: nounwind readnone speculatable willreturn
declare void @llvm.dbg.declare(metadata, metadata, metadata) #2
; Function Attrs: nounwind
declare dso_local i32 @rand() #3
; Function Attrs: argmemonly nounwind willreturn
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
; Function Attrs: nounwind uwtable
define dso_local i32 @main() #0 !dbg !51 {
entry:
%retval = alloca i32, align 4
%val = alloca i32, align 4
%j = alloca i32, align 4
%condition = alloca i32, align 4
store i32 0, i32* %retval, align 4
call void @init_arry(), !dbg !62
%0 = bitcast i32* %val to i8*, !dbg !63
call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #6, !dbg !63
call void @llvm.dbg.declare(metadata i32* %val, metadata !55, metadata !DIExpression()), !dbg !64
store i32 0, i32* %val, align 4, !dbg !64, !tbaa !30
%1 = bitcast i32* %j to i8*, !dbg !65
call void @llvm.lifetime.start.p0i8(i64 4, i8* %1) #6, !dbg !65
call void @llvm.dbg.declare(metadata i32* %j, metadata !56, metadata !DIExpression()), !dbg !66
store i32 0, i32* %j, align 4, !dbg !67, !tbaa !30
br label %for.cond, !dbg !68
for.cond: ; preds = %for.inc, %entry
%2 = load i32, i32* %j, align 4, !dbg !69, !tbaa !30
%cmp = icmp slt i32 %2, 20000, !dbg !70
br i1 %cmp, label %for.body, label %for.end, !dbg !71
for.body: ; preds = %for.cond
%3 = bitcast i32* %condition to i8*, !dbg !72
call void @llvm.lifetime.start.p0i8(i64 4, i8* %3) #6, !dbg !72
call void @llvm.dbg.declare(metadata i32* %condition, metadata !57, metadata !DIExpression()), !dbg !73
%call = call i32 @rand() #6, !dbg !74
%rem = srem i32 %call, 5, !dbg !75
store i32 %rem, i32* %condition, align 4, !dbg !73, !tbaa !30
%4 = load i32, i32* %condition, align 4, !dbg !76, !tbaa !30
%conv = zext i32 %4 to i64, !dbg !76
%expval = call i64 @llvm.expect.i64(i64 %conv, i64 0), !dbg !77
switch i64 %expval, label %sw.default [
i64 0, label %sw.bb
i64 1, label %sw.bb2
i64 2, label %sw.bb2
i64 3, label %sw.bb2
i64 4, label %sw.bb3
], !dbg !78
sw.bb: ; preds = %for.body
%call1 = call i32 @sum(i32* getelementptr inbounds ([25 x i32], [25 x i32]* @arry, i64 0, i64 0), i32 25), !dbg !79
%5 = load i32, i32* %val, align 4, !dbg !81, !tbaa !30
%add = add nsw i32 %5, %call1, !dbg !81
store i32 %add, i32* %val, align 4, !dbg !81, !tbaa !30
br label %sw.epilog, !dbg !82
sw.bb2: ; preds = %for.body, %for.body, %for.body
br label %sw.epilog, !dbg !83
sw.bb3: ; preds = %for.body
%call4 = call i32 @random_sample(i32* getelementptr inbounds ([25 x i32], [25 x i32]* @arry, i64 0, i64 0), i32 25), !dbg !84
%6 = load i32, i32* %val, align 4, !dbg !85, !tbaa !30
%add5 = add nsw i32 %6, %call4, !dbg !85
store i32 %add5, i32* %val, align 4, !dbg !85, !tbaa !30
br label %sw.epilog, !dbg !86
sw.default: ; preds = %for.body
unreachable, !dbg !87
sw.epilog: ; preds = %sw.bb3, %sw.bb2, %sw.bb
%7 = bitcast i32* %condition to i8*, !dbg !88
call void @llvm.lifetime.end.p0i8(i64 4, i8* %7) #6, !dbg !88
br label %for.inc, !dbg !89
for.inc: ; preds = %sw.epilog
%8 = load i32, i32* %j, align 4, !dbg !90, !tbaa !30
%inc = add nsw i32 %8, 1, !dbg !90
store i32 %inc, i32* %j, align 4, !dbg !90, !tbaa !30
br label %for.cond, !dbg !91, !llvm.loop !92
for.end: ; preds = %for.cond
%9 = bitcast i32* %j to i8*, !dbg !94
call void @llvm.lifetime.end.p0i8(i64 4, i8* %9) #6, !dbg !94
%10 = bitcast i32* %val to i8*, !dbg !94
call void @llvm.lifetime.end.p0i8(i64 4, i8* %10) #6, !dbg !94
ret i32 0, !dbg !95
}
; Function Attrs: nounwind readnone willreturn
declare i64 @llvm.expect.i64(i64, i64) #4
declare dso_local i32 @sum(i32*, i32) #5
declare dso_local i32 @random_sample(i32*, i32) #5
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" }
attributes #1 = { argmemonly nounwind willreturn }
attributes #2 = { nounwind readnone speculatable willreturn }
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" }
attributes #4 = { nounwind readnone willreturn }
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" }
attributes #6 = { nounwind }
!llvm.dbg.cu = !{!2}
!llvm.module.flags = !{!17, !18, !19}
!llvm.ident = !{!20}
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "inner_loop", scope: !2, file: !3, line: 7, type: !8, isLocal: false, isDefinition: true)
!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None)
!3 = !DIFile(filename: "misexpect-switch.c", directory: ".")
!4 = !{}
!5 = !{!0, !6, !10, !12}
!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression())
!7 = distinct !DIGlobalVariable(name: "outer_loop", scope: !2, file: !3, line: 8, type: !8, isLocal: false, isDefinition: true)
!8 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !9)
!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!10 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression())
!11 = distinct !DIGlobalVariable(name: "arry_size", scope: !2, file: !3, line: 9, type: !8, isLocal: false, isDefinition: true)
!12 = !DIGlobalVariableExpression(var: !13, expr: !DIExpression())
!13 = distinct !DIGlobalVariable(name: "arry", scope: !2, file: !3, line: 11, type: !14, isLocal: false, isDefinition: true)
!14 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 800, elements: !15)
!15 = !{!16}
!16 = !DISubrange(count: 25)
!17 = !{i32 2, !"Dwarf Version", i32 4}
!18 = !{i32 2, !"Debug Info Version", i32 3}
!19 = !{i32 1, !"wchar_size", i32 4}
!20 = !{!"clang version 10.0.0"}
!21 = distinct !DISubprogram(name: "init_arry", scope: !3, file: !3, line: 13, type: !22, scopeLine: 13, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !24)
!22 = !DISubroutineType(types: !23)
!23 = !{null}
!24 = !{!25}
!25 = !DILocalVariable(name: "i", scope: !21, file: !3, line: 14, type: !9)
!26 = !DILocation(line: 14, column: 3, scope: !21)
!27 = !DILocation(line: 14, column: 7, scope: !21)
!28 = !DILocation(line: 15, column: 10, scope: !29)
!29 = distinct !DILexicalBlock(scope: !21, file: !3, line: 15, column: 3)
!30 = !{!31, !31, i64 0}
!31 = !{!"int", !32, i64 0}
!32 = !{!"omnipotent char", !33, i64 0}
!33 = !{!"Simple C/C++ TBAA"}
!34 = !DILocation(line: 15, column: 8, scope: !29)
!35 = !DILocation(line: 15, column: 15, scope: !36)
!36 = distinct !DILexicalBlock(scope: !29, file: !3, line: 15, column: 3)
!37 = !DILocation(line: 15, column: 17, scope: !36)
!38 = !DILocation(line: 15, column: 3, scope: !29)
!39 = !DILocation(line: 16, column: 15, scope: !40)
!40 = distinct !DILexicalBlock(scope: !36, file: !3, line: 15, column: 35)
!41 = !DILocation(line: 16, column: 22, scope: !40)
!42 = !DILocation(line: 16, column: 10, scope: !40)
!43 = !DILocation(line: 16, column: 5, scope: !40)
!44 = !DILocation(line: 16, column: 13, scope: !40)
!45 = !DILocation(line: 17, column: 3, scope: !40)
!46 = !DILocation(line: 15, column: 30, scope: !36)
!47 = !DILocation(line: 15, column: 3, scope: !36)
!48 = distinct !{!48, !38, !49}
!49 = !DILocation(line: 17, column: 3, scope: !29)
!50 = !DILocation(line: 18, column: 1, scope: !21)
!51 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 20, type: !52, scopeLine: 20, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !54)
!52 = !DISubroutineType(types: !53)
!53 = !{!9}
!54 = !{!55, !56, !57}
!55 = !DILocalVariable(name: "val", scope: !51, file: !3, line: 22, type: !9)
!56 = !DILocalVariable(name: "j", scope: !51, file: !3, line: 23, type: !9)
!57 = !DILocalVariable(name: "condition", scope: !58, file: !3, line: 25, type: !61)
!58 = distinct !DILexicalBlock(scope: !59, file: !3, line: 24, column: 49)
!59 = distinct !DILexicalBlock(scope: !60, file: !3, line: 24, column: 3)
!60 = distinct !DILexicalBlock(scope: !51, file: !3, line: 24, column: 3)
!61 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
!62 = !DILocation(line: 21, column: 3, scope: !51)
!63 = !DILocation(line: 22, column: 3, scope: !51)
!64 = !DILocation(line: 22, column: 7, scope: !51)
!65 = !DILocation(line: 23, column: 3, scope: !51)
!66 = !DILocation(line: 23, column: 7, scope: !51)
!67 = !DILocation(line: 24, column: 10, scope: !60)
!68 = !DILocation(line: 24, column: 8, scope: !60)
!69 = !DILocation(line: 24, column: 15, scope: !59)
!70 = !DILocation(line: 24, column: 17, scope: !59)
!71 = !DILocation(line: 24, column: 3, scope: !60)
!72 = !DILocation(line: 25, column: 5, scope: !58)
!73 = !DILocation(line: 25, column: 14, scope: !58)
!74 = !DILocation(line: 25, column: 26, scope: !58)
!75 = !DILocation(line: 25, column: 33, scope: !58)
!76 = !DILocation(line: 26, column: 30, scope: !58)
!77 = !DILocation(line: 26, column: 13, scope: !58)
!78 = !DILocation(line: 26, column: 5, scope: !58)
!79 = !DILocation(line: 28, column: 14, scope: !80)
!80 = distinct !DILexicalBlock(scope: !58, file: !3, line: 26, column: 45)
!81 = !DILocation(line: 28, column: 11, scope: !80)
!82 = !DILocation(line: 29, column: 7, scope: !80)
!83 = !DILocation(line: 33, column: 7, scope: !80)
!84 = !DILocation(line: 35, column: 14, scope: !80)
!85 = !DILocation(line: 35, column: 11, scope: !80)
!86 = !DILocation(line: 36, column: 7, scope: !80)
!87 = !DILocation(line: 38, column: 7, scope: !80)
!88 = !DILocation(line: 40, column: 3, scope: !59)
!89 = !DILocation(line: 40, column: 3, scope: !58)
!90 = !DILocation(line: 24, column: 44, scope: !59)
!91 = !DILocation(line: 24, column: 3, scope: !59)
!92 = distinct !{!92, !71, !93}
!93 = !DILocation(line: 40, column: 3, scope: !60)
!94 = !DILocation(line: 43, column: 1, scope: !51)
!95 = !DILocation(line: 42, column: 3, scope: !51)