Commit 03b8d6ea by Abseil Team Committed by Copybara-Service

Improve ABSL_ASSERT performance by guaranteeing it is optimized away under NDEBUG in C++20

Also fix the old definition by verifying that the condition is contextually convertible to bool.

The new C++20 definition reduces codegen bloat and guarantees fast performance while still retaining the expression (so that unused-variable warnings don't trigger).

As an example where this makes a difference, compare the following snippet under `-DABSL_INTERNAL_CPLUSPLUS_LANG=202002`: https://godbolt.org/z/hjf59n84v
```
#include <stdlib.h>
template<class T> struct S { S() { abort(); } static S const s; };
template<class T> S<T> const S<T>::s = {};
#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
#define ABSL_ASSERT(expr) (decltype((expr) ? void() : void())())
#else
#define ABSL_ASSERT(expr) (false ? ((expr) ? void() : void()) : void())
#endif
void foo() { ABSL_ASSERT(((void)&S<int>::s, true)); }
```

We see that, in unoptimized builds, code is still generated with the old definition of `ABSL_ASSERT`. Moreover, even under optimizations (with `-O2`), the call to abort() still lingers with the old definition of `ABSL_ASSERT`.

Therefore the extra generated code can affect both compile- and run-time performance.

PiperOrigin-RevId: 681563573
Change-Id: I7fbfadcc7fd198e8e1daf14615c33687f6b23af7
parent 5d347078
...@@ -82,8 +82,9 @@ ABSL_NAMESPACE_END ...@@ -82,8 +82,9 @@ ABSL_NAMESPACE_END
// ABSL_ASSERT() // ABSL_ASSERT()
// //
// In C++11, `assert` can't be used portably within constexpr functions. // In C++11, `assert` can't be used portably within constexpr functions.
// `assert` also generates spurious unused-symbol warnings.
// ABSL_ASSERT functions as a runtime assert but works in C++11 constexpr // ABSL_ASSERT functions as a runtime assert but works in C++11 constexpr
// functions. Example: // functions, and maintains references to symbols. Example:
// //
// constexpr double Divide(double a, double b) { // constexpr double Divide(double a, double b) {
// return ABSL_ASSERT(b != 0), a / b; // return ABSL_ASSERT(b != 0), a / b;
...@@ -92,8 +93,18 @@ ABSL_NAMESPACE_END ...@@ -92,8 +93,18 @@ ABSL_NAMESPACE_END
// This macro is inspired by // This macro is inspired by
// https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/ // https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/
#if defined(NDEBUG) #if defined(NDEBUG)
#define ABSL_ASSERT(expr) \ #if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
(false ? static_cast<void>(expr) : static_cast<void>(0)) // We use `decltype` here to avoid generating unnecessary code that the
// optimizer then has to optimize away.
// This not only improves compilation performance by reducing codegen bloat
// and optimization work, but also guarantees fast run-time performance without
// having to rely on the optimizer.
#define ABSL_ASSERT(expr) (decltype((expr) ? void() : void())())
#else
// Pre-C++20, lambdas can't be inside unevaluated operands, so we're forced to
// rely on the optimizer.
#define ABSL_ASSERT(expr) (false ? ((expr) ? void() : void()) : void())
#endif
#else #else
#define ABSL_ASSERT(expr) \ #define ABSL_ASSERT(expr) \
(ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) \ (ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment