Skip to content

Commit

Permalink
Improve ABSL_ASSERT performance by guaranteeing it is optimized away …
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
Abseil Team authored and copybara-github committed Oct 2, 2024
1 parent 5d34707 commit 03b8d6e
Showing 1 changed file with 14 additions and 3 deletions.
17 changes: 14 additions & 3 deletions absl/base/macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ ABSL_NAMESPACE_END
// ABSL_ASSERT()
//
// 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
// functions. Example:
// functions, and maintains references to symbols. Example:
//
// constexpr double Divide(double a, double b) {
// return ABSL_ASSERT(b != 0), a / b;
Expand All @@ -92,8 +93,18 @@ ABSL_NAMESPACE_END
// This macro is inspired by
// https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/
#if defined(NDEBUG)
#define ABSL_ASSERT(expr) \
(false ? static_cast<void>(expr) : static_cast<void>(0))
#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
// 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
#define ABSL_ASSERT(expr) \
(ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) \
Expand Down

0 comments on commit 03b8d6e

Please sign in to comment.