From 93bfabc8b89270c00816c3cafb601475eaf85364 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 2 Jun 2017 16:47:24 -0600 Subject: [PATCH] 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 --- include/benchmark/benchmark_api.h | 6 ++++++ test/CMakeLists.txt | 6 ++++++ test/donotoptimize_test.cc | 21 ++++++++++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/include/benchmark/benchmark_api.h b/include/benchmark/benchmark_api.h index 1e853e2c..47553221 100644 --- a/include/benchmark/benchmark_api.h +++ b/include/benchmark/benchmark_api.h @@ -231,7 +231,13 @@ BENCHMARK_UNUSED static int stream_init_anchor = InitializeStreams(); #ifndef BENCHMARK_HAS_NO_INLINE_ASSEMBLY template 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"); +#else + asm volatile("" : : "i,r,m"(value) : "memory"); +#endif } // Force the compiler to flush pending writes to global memory. Acts as an // effective read/write barrier diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d89135aa..b55612b4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,7 @@ # Enable the tests find_package(Threads REQUIRED) +include(CheckCXXCompilerFlag) # NOTE: Some tests use `` to perform the test. Therefore we must # 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) 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) compile_benchmark_test(fixture_test) diff --git a/test/donotoptimize_test.cc b/test/donotoptimize_test.cc index b21187aa..a705654a 100644 --- a/test/donotoptimize_test.cc +++ b/test/donotoptimize_test.cc @@ -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; } } +// 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*[]) { // this test verifies compilation of DoNotOptimize() for some types @@ -29,5 +45,8 @@ int main(int, char*[]) { benchmark::DoNotOptimize(double_up(x)); - return 0; + // These tests are to e + benchmark::DoNotOptimize(BitRef::Make()); + BitRef lval = BitRef::Make(); + benchmark::DoNotOptimize(lval); }