forked from google/benchmark
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add MakeUnpredictable to hide an objects value from the optimizer.
This patch addresses issue google#341 by adding the function MakeUnpredictable. The MakeUnpredictable(...) functions can be used to prevent the optimizer from knowing the value of the specified 'object'. The function returns the "laundered" input, either by reference if the input was a non-const lvalue reference, or by value (when the input was a const lvalue or rvalue). When MakeUnpredictable is supplied a non-const lvalue, the object referenced by the input is made unpredictable and the return value can be ignored. Otherwise, only the return value is considered "unpredictable". In the latter the MakeUnpredictable function is marked [[nodiscard]] and a warning is emitted if the return value is ignored. For example: ```c++ int divide_by_two(int Value) { const int Divisor = 2; DoNotOptimize(Divisor); // INCORRECT! Has no effect on Divisor MakeUnpredictable(Divisor); // INCORRECT! should warn that return is ignored. // Correct Usage google#1 return Value / MakeUnpredictable(Divisor); // Correct Usage google#2 const int Divisor = MakeUnpredictable(2); return Value / Divisor; } ```
- Loading branch information
Showing
5 changed files
with
218 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#include <benchmark/benchmark.h> | ||
|
||
// CHECK-LABEL: test_div_by_two_lvalue: | ||
extern "C" int test_div_by_two_lvalue(int input) { | ||
int divisor = 2; | ||
benchmark::MakeUnpredictable(divisor); | ||
return input / divisor; | ||
// CHECK: movl $2, [[DEST:.*]] | ||
// CHECK: idivl [[DEST]] | ||
// CHECK: ret | ||
} | ||
|
||
// CHECK-LABEL: test_div_by_two_rvalue: | ||
extern "C" int test_div_by_two_rvalue(int input) { | ||
int divisor = benchmark::MakeUnpredictable(2); | ||
return input / divisor; | ||
// CHECK: movl $2, [[DEST:.*]] | ||
// CHECK: idivl [[DEST]] | ||
// CHECK: ret | ||
} | ||
|
||
// CHECK-LABEL: test_div_by_two_rvalue_2: | ||
extern "C" int test_div_by_two_rvalue_2(int input) { | ||
return input / benchmark::MakeUnpredictable(2); | ||
// CHECK: movl $2, [[DEST:.*]] | ||
// CHECK: idivl [[DEST]] | ||
// CHECK: ret | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
#include "benchmark/benchmark.h" | ||
|
||
#include <cstdint> | ||
#include <type_traits> | ||
|
||
namespace { | ||
#if defined(__GNUC__) | ||
std::uint64_t double_up(const std::uint64_t x) __attribute__((const)); | ||
#endif | ||
std::uint64_t double_up(const std::uint64_t x) { return x * 2; } | ||
} | ||
|
||
// Using MakeUnpredictable on types like BitRef seem to cause a lot of problems | ||
// with the inline assembly on both GCC and Clang. | ||
struct BitRef { | ||
int index; | ||
unsigned char &byte; | ||
|
||
public: | ||
static BitRef Make() { | ||
static unsigned char arr[2] = {}; | ||
BitRef b(1, arr[0]); | ||
return b; | ||
} | ||
private: | ||
BitRef(int i, unsigned char& b) : index(i), byte(b) {} | ||
}; | ||
|
||
struct MoveOnly { | ||
explicit MoveOnly(int xvalue) : value(xvalue) {} | ||
MoveOnly(MoveOnly&& other) : value(other.value) { | ||
other.value = -1; | ||
} | ||
int value; | ||
}; | ||
|
||
using benchmark::MakeUnpredictable; | ||
|
||
#define UNUSED (void) | ||
|
||
void verify_compile() { | ||
// this test verifies compilation of MakeUnpredictable() for some types | ||
|
||
char buffer8[8]; | ||
MakeUnpredictable(buffer8); | ||
|
||
char buffer20[20]; | ||
MakeUnpredictable(buffer20); | ||
|
||
char buffer1024[1024]; | ||
MakeUnpredictable(buffer1024); | ||
UNUSED MakeUnpredictable(&buffer1024[0]); | ||
|
||
int x = 123; | ||
MakeUnpredictable(x); | ||
UNUSED MakeUnpredictable(&x); | ||
UNUSED MakeUnpredictable(x += 42); | ||
|
||
UNUSED MakeUnpredictable(double_up(x)); | ||
|
||
// These tests are to e | ||
UNUSED MakeUnpredictable(BitRef::Make()); | ||
BitRef lval = BitRef::Make(); | ||
MakeUnpredictable(lval); | ||
} | ||
#undef UNUSED | ||
|
||
#define ASSERT_TYPE(Expr, Expect) \ | ||
static_assert(std::is_same<decltype(MakeUnpredictable(Expr)), Expect>::value, "") | ||
|
||
void verify_return_type() { | ||
{ | ||
int lvalue = 42; | ||
ASSERT_TYPE(lvalue, int&); | ||
int &result = MakeUnpredictable(lvalue); | ||
assert(&result == &lvalue); | ||
assert(lvalue == 42); | ||
} | ||
{ | ||
const int clvalue = 42; | ||
ASSERT_TYPE(clvalue, int); | ||
assert(MakeUnpredictable(clvalue) == 42); | ||
} | ||
{ | ||
ASSERT_TYPE(42, int); | ||
assert(MakeUnpredictable(42) == 42); | ||
} | ||
{ | ||
int rvalue = -1; | ||
ASSERT_TYPE(std::move(rvalue), int); | ||
int result = MakeUnpredictable(std::move(rvalue)); | ||
assert(rvalue == -1); | ||
assert(result == -1); | ||
} | ||
{ | ||
const int const_rvalue = 42; | ||
ASSERT_TYPE(std::move(const_rvalue), int); | ||
int result = MakeUnpredictable(std::move(const_rvalue)); | ||
assert(const_rvalue == 42); | ||
assert(result == 42); | ||
} | ||
{ | ||
MoveOnly mv(42); | ||
ASSERT_TYPE(std::move(mv), MoveOnly); | ||
MoveOnly result = MakeUnpredictable(std::move(mv)); | ||
assert(result.value == 42); | ||
assert(mv.value == -1); // We moved from it during MakeUnpredictable | ||
} | ||
} | ||
|
||
int main() { | ||
verify_compile(); | ||
verify_return_type(); | ||
} |