Clarify DoNotOptimize const-ref warning (#2201)
Improve docs for `DoNotOptimize()`, and make it's compile-time diagnostic suggest a possible fix
diff --git a/docs/user_guide.md b/docs/user_guide.md
index 14e2004..3fed926 100644
--- a/docs/user_guide.md
+++ b/docs/user_guide.md
@@ -1462,6 +1462,22 @@
// while (...) DoNotOptimize(__result__);
```
+Since that is not the behaviour the user intended,
+such problematic cases will result in a diagnostic
+at compile time. The correct approach, for an expression result,
+is to use an intermediate local variable:
+
+```c++
+ // Avoid: may call the deprecated const-reference overload.
+ while (...) DoNotOptimize(foo(0));
+
+ // Prefer: materialize the result, then pass the local lvalue.
+ while (...) {
+ auto result = foo(0);
+ DoNotOptimize(result);
+ }
+```
+
The second tool for preventing optimizations is `ClobberMemory()`. In essence
`ClobberMemory()` forces the compiler to perform all pending writes to global
memory. Memory managed by block scope objects must be "escaped" using
diff --git a/include/benchmark/utils.h b/include/benchmark/utils.h
index 0e4d95d..dccd3b2 100644
--- a/include/benchmark/utils.h
+++ b/include/benchmark/utils.h
@@ -37,12 +37,15 @@
std::atomic_signal_fence(std::memory_order_acq_rel);
}
+#define BENCHMARK_DONOTOPTIMIZE_CONST_REF_DEPRECATED_MSG \
+ "DoNotOptimize(T const&) can permit undesired compiler optimizations. " \
+ "Pass a non-const lvalue instead; if the argument is an expression result, " \
+ "store it in a local variable first."
+
#ifndef BENCHMARK_HAS_NO_INLINE_ASSEMBLY
#if !defined(__GNUC__) || defined(__llvm__) || defined(__INTEL_COMPILER)
template <class Tp>
-BENCHMARK_DEPRECATED_MSG(
- "The const-ref version of this method can permit "
- "undesired compiler optimizations in benchmarks")
+BENCHMARK_DEPRECATED_MSG(BENCHMARK_DONOTOPTIMIZE_CONST_REF_DEPRECATED_MSG)
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
asm volatile("" : : "r,m"(value) : "memory");
}
@@ -66,9 +69,7 @@
}
#elif (__GNUC__ >= 5)
template <class Tp>
-BENCHMARK_DEPRECATED_MSG(
- "The const-ref version of this method can permit "
- "undesired compiler optimizations in benchmarks")
+BENCHMARK_DEPRECATED_MSG(BENCHMARK_DONOTOPTIMIZE_CONST_REF_DEPRECATED_MSG)
inline BENCHMARK_ALWAYS_INLINE
typename std::enable_if<std::is_trivially_copyable<Tp>::value &&
(sizeof(Tp) <= sizeof(Tp*))>::type
@@ -77,9 +78,7 @@
}
template <class Tp>
-BENCHMARK_DEPRECATED_MSG(
- "The const-ref version of this method can permit "
- "undesired compiler optimizations in benchmarks")
+BENCHMARK_DEPRECATED_MSG(BENCHMARK_DONOTOPTIMIZE_CONST_REF_DEPRECATED_MSG)
inline BENCHMARK_ALWAYS_INLINE
typename std::enable_if<!std::is_trivially_copyable<Tp>::value ||
(sizeof(Tp) > sizeof(Tp*))>::type
@@ -122,9 +121,7 @@
#elif defined(_MSC_VER)
template <class Tp>
-BENCHMARK_DEPRECATED_MSG(
- "The const-ref version of this method can permit "
- "undesired compiler optimizations in benchmarks")
+BENCHMARK_DEPRECATED_MSG(BENCHMARK_DONOTOPTIMIZE_CONST_REF_DEPRECATED_MSG)
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
internal::UseCharPointer(&reinterpret_cast<char const volatile&>(value));
ClobberMemory();
@@ -148,6 +145,8 @@
}
#endif
+#undef BENCHMARK_DONOTOPTIMIZE_CONST_REF_DEPRECATED_MSG
+
} // end namespace benchmark
#endif // BENCHMARK_UTILS_H_