Skip to content

Fix DoNotOptimize compile failure on types with const members#2254

Open
devtejasx wants to merge 2 commits into
google:mainfrom
devtejasx:fix-donotoptimize-const-members
Open

Fix DoNotOptimize compile failure on types with const members#2254
devtejasx wants to merge 2 commits into
google:mainfrom
devtejasx:fix-donotoptimize-const-members

Conversation

@devtejasx

@devtejasx devtejasx commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Fixes #1997

Problem

GCC and Clang both reject objects of class types with const data members as asm output operands — such objects cannot be stored to:

gcc:   error: read-only reference 'value' used as 'asm' output
clang: error: invalid lvalue in asm output

Since the const& overload of DoNotOptimize is deprecated, there was no usable spelling at all for these types when reached through a deduced non-const reference ([](auto& v) { DoNotOptimize(v); }), as reported in #1997.

Fix

Add an internal::IsAsmOutputOperand trait and route non-output-capable types to an input-only constraint with a memory clobber. Such objects cannot be legally written through anyway, so no optimization barrier is lost relative to the read-write constraint.

There is no portable trait for "contains a const member", so the trait approximates it as "not assignable" (is_array || is_copy_assignable || is_move_assignable):

  • types with const members → correctly take the fallback (they'd fail to compile as outputs)
  • reference-member types (e.g. the existing BitRef test type) are actually valid asm outputs but are indistinguishable by trait — they conservatively take the same fallback, which still compiles and keeps the clobber
  • arrays are not assignable but are valid memory outputs, so they're accepted unconditionally

Both the clang/ICC branch and the GCC ≥ 5 branch get the dispatch; the GCC branch keeps its existing register-size/trivially-copyable split in the fallbacks ("r,m" vs "m", mirroring the deprecated const-ref overloads). MSVC needs no change (no inline asm).

Testing

  • The reproducer from [BUG] DoNotOptimize on values with const members again #1997 now compiles with GCC 15 and Clang 21 (it fails with both on current main)
  • donotoptimize_test.cc extended with const-member types: register-sized and larger, as lvalues, rvalues, and through a deduced non-const reference; builds and runs clean with -Wall -Wextra on GCC and Clang, -O1 and -O3

Disclosure

Per AGENTS.md: this PR was developed with AI assistance (Claude Code). The change and analysis have been reviewed and are owned by me.

GCC and Clang reject objects of class types with const data members as
asm output operands, since such objects cannot be stored to. This made
DoNotOptimize(x) fail to compile for any such type when reached through
a deduced non-const reference (issue google#1997), with no usable alternative
because the const-ref overload is deprecated.

Dispatch on assignability - the closest portable approximation of
contains-no-const-member - and route non-assignable types to an
input-only constraint with a memory clobber. Such objects cannot be
legally written through anyway, so no optimization barrier is lost.
Reference-member types (valid outputs, but indistinguishable by trait)
conservatively take the same fallback.

Fixes google#1997
Exercises DoNotOptimize with register-sized and larger-than-register
types holding const data members, as lvalues, rvalues, and through a
deduced non-const reference (the reproducer from issue google#1997).
@dmah42

dmah42 commented Jul 16, 2026

Copy link
Copy Markdown
Member

just going to put this here first: https://github.com/google/benchmark/blob/main/AGENTS.md

please explain why this is such a big problem that we need such complex code to be introduced to the library.

@devtejasx

Copy link
Copy Markdown
Contributor Author

Disclosure, per AGENTS.md: this PR was developed with AI assistance (Claude Code). I've reviewed the change and the analysis, and I stand behind both — happy to argue any part of it on the merits.

On the substance — why this is a real problem:

DoNotOptimize currently cannot be used at all on a type with a const data member. The non-const overloads fail to compile (GCC: "read-only reference used as 'asm' output", Clang: "invalid lvalue in asm output"), and the const& overload is deprecated, so there is no non-warning spelling left. The failure mode bites hardest in generic code — the reproducer in #1997 is a lambda taking auto&, where the caller doesn't control the type being benchmarked.

Why the patch looks the way it does: the GCC branch already dispatches on trivially-copyable × register-size (8 overloads before this change). The patch adds one boolean trait to that existing matrix, and the fallbacks reuse the exact constraints of the existing deprecated const& overloads — no new mechanism.

If you'd prefer a smaller surface, two options I'd be fine with:

  1. collapse the fallbacks to a single "m"-input overload per value category (register-sized const-member types lose the "r" alternative — negligible in practice), or
  2. reject this class of types with a static_assert and a clear message instead of supporting them — smaller patch, but leaves [BUG] DoNotOptimize on values with const members again #1997 unfixed for generic code.

Happy to rework in either direction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] DoNotOptimize on values with const members again

2 participants