Fix #342: DoNotOptimize causes compile errors on older GCC versions. (#398)

* Fix #342: DoNotOptimize causes compile errors on older GCC versions.

DoNotOptimize uses inline assembly contraints to tell
the compiler what the type of the input variable. The 'g'
operand allows the input to be any register, memory, or
immediate integer operand. However this constraint seems
to be too weak on older GCC versions, and certain inputs
will cause compile errors.

This patch changes the constraint to 'X', which is documented
as "any operand whatsoever is allowed". This appears to fix
the issues with older GCC versions.

However Clang doesn't seem to like "X", and will attempt
to put the input into a register even when it can't/shouldn't;
causing a compile error. However using "g" seems to work like
"X" with GCC, so for this reason Clang still uses "g".

* Try alternative formulation to placate GCC
This commit is contained in:
Eric 2017-06-02 16:47:24 -06:00 committed by Dominic Hamon
parent 15e9ebaf83
commit 93bfabc8b8
3 changed files with 32 additions and 1 deletions

View File

@ -231,7 +231,13 @@ BENCHMARK_UNUSED static int stream_init_anchor = InitializeStreams();
#ifndef BENCHMARK_HAS_NO_INLINE_ASSEMBLY #ifndef BENCHMARK_HAS_NO_INLINE_ASSEMBLY
template <class Tp> template <class Tp>
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) { inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
// Clang doesn't like the 'X' constraint on `value` and certain GCC versions
// don't like the 'g' constraint. Attempt to placate them both.
#if defined(__clang__)
asm volatile("" : : "g"(value) : "memory"); asm volatile("" : : "g"(value) : "memory");
#else
asm volatile("" : : "i,r,m"(value) : "memory");
#endif
} }
// Force the compiler to flush pending writes to global memory. Acts as an // Force the compiler to flush pending writes to global memory. Acts as an
// effective read/write barrier // effective read/write barrier

View File

@ -1,6 +1,7 @@
# Enable the tests # Enable the tests
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
include(CheckCXXCompilerFlag)
# NOTE: Some tests use `<cassert>` to perform the test. Therefore we must # NOTE: Some tests use `<cassert>` to perform the test. Therefore we must
# strip -DNDEBUG from the default CMake flags in DEBUG mode. # strip -DNDEBUG from the default CMake flags in DEBUG mode.
@ -75,6 +76,11 @@ compile_benchmark_test(skip_with_error_test)
add_test(skip_with_error_test skip_with_error_test --benchmark_min_time=0.01) add_test(skip_with_error_test skip_with_error_test --benchmark_min_time=0.01)
compile_benchmark_test(donotoptimize_test) compile_benchmark_test(donotoptimize_test)
# Some of the issues with DoNotOptimize only occur when optimization is enabled
check_cxx_compiler_flag(-O3 BENCHMARK_HAS_O3_FLAG)
if (BENCHMARK_HAS_O3_FLAG)
set_target_properties(donotoptimize_test PROPERTIES COMPILE_FLAGS "-O3")
endif()
add_test(donotoptimize_test donotoptimize_test --benchmark_min_time=0.01) add_test(donotoptimize_test donotoptimize_test --benchmark_min_time=0.01)
compile_benchmark_test(fixture_test) compile_benchmark_test(fixture_test)

View File

@ -9,6 +9,22 @@ std::uint64_t double_up(const std::uint64_t x) __attribute__((const));
std::uint64_t double_up(const std::uint64_t x) { return x * 2; } std::uint64_t double_up(const std::uint64_t x) { return x * 2; }
} }
// Using DoNotOptimize 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) {}
};
int main(int, char*[]) { int main(int, char*[]) {
// this test verifies compilation of DoNotOptimize() for some types // this test verifies compilation of DoNotOptimize() for some types
@ -29,5 +45,8 @@ int main(int, char*[]) {
benchmark::DoNotOptimize(double_up(x)); benchmark::DoNotOptimize(double_up(x));
return 0; // These tests are to e
benchmark::DoNotOptimize(BitRef::Make());
BitRef lval = BitRef::Make();
benchmark::DoNotOptimize(lval);
} }