llvm-project/clang/test/Profile/cxx-hash-v2.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

178 lines
2.7 KiB
C++
Raw Normal View History

[PGO] Detect more structural changes with the stable hash Lifting from Bob Wilson's notes: The hash value that we compute and store in PGO profile data to detect out-of-date profiles does not include enough information. This means that many significant changes to the source will not cause compiler warnings about the profile being out of date, and worse, we may continue to use the outdated profile data to make bad optimization decisions. There is some tension here because some source changes won't affect PGO and we don't want to invalidate the profile unnecessarily. This patch adds a new hashing scheme which is more sensitive to loop nesting, conditions, and out-of-order control flow. Here are examples which show snippets which get the same hash under the current scheme, and different hashes under the new scheme: Loop Nesting Example -------------------- // Snippet 1 while (foo()) { while (bar()) {} } // Snippet 2 while (foo()) {} while (bar()) {} Condition Example ----------------- // Snippet 1 if (foo()) bar(); baz(); // Snippet 2 if (foo()) bar(); else baz(); Out-of-order Control Flow Example --------------------------------- // Snippet 1 while (foo()) { if (bar()) {} baz(); } // Snippet 2 while (foo()) { if (bar()) continue; baz(); } In each of these cases, it's useful to differentiate between the snippets because swapping their profiles gives bad optimization hints. The new hashing scheme considers some logical operators in an effort to detect more changes in conditions. This isn't a perfect scheme. E.g, it does not produce the same hash for these equivalent snippets: // Snippet 1 bool c = !a || b; if (d && e) {} // Snippet 2 bool f = d && e; bool c = !a || b; if (f) {} This would require an expensive data flow analysis. Short of that, the new hashing scheme looks reasonably complete, based on a scan over the statements we place counters on. Profiles which use the old version of the PGO hash remain valid and can be used without issue (there are tests in tree which check this). rdar://17068282 Differential Revision: https://reviews.llvm.org/D39446 llvm-svn: 318229
2017-11-15 07:56:53 +08:00
// REQUIRES: shell
// Check that all of the hashes in this file are unique (i.e, that none of the
// profiles for these functions are mutually interchangeable).
//
// RUN: llvm-profdata show -all-functions %S/Inputs/cxx-hash-v2.profdata.v5 | grep "Hash: 0x" | sort > %t.hashes
// RUN: uniq %t.hashes > %t.hashes.unique
// RUN: diff %t.hashes %t.hashes.unique
// RUN: llvm-profdata merge %S/Inputs/cxx-hash-v2.proftext -o %t.profdata
// RUN: %clang_cc1 -std=c++11 -fexceptions -fcxx-exceptions -triple x86_64-apple-macosx10.9 -main-file-name cxx-hash-v2.mm %s -o /dev/null -emit-llvm -fprofile-instrument-use-path=%t.profdata 2>&1 | FileCheck %s -allow-empty
// RUN: %clang_cc1 -std=c++11 -fexceptions -fcxx-exceptions -triple x86_64-apple-macosx10.9 -main-file-name cxx-hash-v2.mm %s -o /dev/null -emit-llvm -fprofile-instrument-use-path=%S/Inputs/cxx-hash-v2.profdata.v5 2>&1 | FileCheck %s -allow-empty
// CHECK-NOT: warning: profile data may be out of date
int x;
int arr[1] = {0};
void loop_after_if_else() {
if (1)
x = 1;
else
x = 2;
while (0)
++x;
}
void loop_in_then_block() {
if (1) {
while (0)
++x;
} else {
x = 2;
}
}
void loop_in_else_block() {
if (1) {
x = 1;
} else {
while (0)
++x;
}
}
void if_inside_of_for() {
for (x = 0; x < 0; ++x) {
x = 1;
if (1)
x = 2;
}
}
void if_outside_of_for() {
for (x = 0; x < 0; ++x)
x = 1;
if (1)
x = 2;
}
void if_inside_of_while() {
while (0) {
x = 1;
if (1)
x = 2;
}
}
void if_outside_of_while() {
while (0)
x = 1;
if (1)
x = 2;
}
void nested_dos() {
do {
do {
++x;
} while (0);
} while (0);
}
void consecutive_dos() {
do {
} while (0);
do {
++x;
} while (0);
}
void loop_empty() {
for (x = 0; x < 5; ++x) {}
}
void loop_return() {
for (x = 0; x < 5; ++x)
return;
}
void loop_continue() {
for (x = 0; x < 5; ++x)
continue;
}
void loop_break() {
for (x = 0; x < 5; ++x)
break;
}
void no_gotos() {
static void *dispatch[] = {&&done};
x = 0;
done:
++x;
}
void direct_goto() {
static void *dispatch[] = {&&done};
x = 0;
goto done;
done:
++x;
}
void indirect_goto() {
static void *dispatch[] = {&&done};
x = 0;
goto *dispatch[x];
done:
++x;
}
void nested_for_ranges() {
for (int a : arr)
for (int b : arr)
++x;
}
void consecutive_for_ranges() {
for (int a : arr) {}
for (int b : arr)
++x;
}
void nested_try_catch() {
try {
try {
++x;
} catch (...) {}
} catch (...) {}
}
void consecutive_try_catch() {
try {} catch (...) {}
try {
++x;
} catch (...) {}
}
void no_throw() {}
void has_throw() {
throw 0;
}
void single_lnot() {
if (!x) {}
}
void double_lnot() {
if (!!x) {}
}
int main() {
return 0;
}