[PATCH] D54966: Implement P1007R3 `std::assume_aligned`

Richard Smith - zygoloid via Phabricator reviews at reviews.llvm.org
Mon Dec 3 13:56:37 PST 2018

rsmith added a comment.

In D54966#1314038 <https://reviews.llvm.org/D54966#1314038>, @EricWF wrote:

> We don't need to use `__builtin_assume_aligned` to implement this.  We can use `__attribute__((assume_aligned(N)))` instead. That solves our constexpr problem.

More than that, we **can't** use `__builtin_assume_aligned`, as currently implemented in Clang and GCC, to implement this -- it is non-constant if it cannot **prove** that the pointer is properly-aligned. The library specification for `std::assume_aligned` doesn't permit that behavior, and instead appears to require that a call is a constant subexpression whenever `p` is properly aligned (even if that can't be determined until runtime), meaning that it must also be a constant subexpression in cases where you cannot prove alignment one way or the other. Example:

  extern char p; // might be defined with `alignas(16)`
  extern char *q;
  char *r = q; // required to be initialized to `&p` if `p` is properly aligned, may be `&p` or `nullptr` otherwise
  char *q = std::assume_aligned<16>(&p); // required to be statically initialized if `p` is properly aligned
  int main() { assert((uintptr_t)&p & 15 || r == &q); }

If you use `__builtin_assume_aligned` to implement `std::assume_aligned`, the above assertion can fail, because `&p` can't be proven to be properly-aligned. But if you use the attribute, then no checking will be done during constant evaluation, and the assertion will always pass. (Try it out yourself: https://godbolt.org/z/YKIdWF -- try changing the alignment from 1 to 2 and back, and observe that `q` switches between static and dynamic initialization, and `r` switches from being initialized to `&p` to being initialized to `nullptr`, even though the compiler does not know that `p` will not be 2-byte aligned.)

It's OK that `assume_aligned` is a constant subexpression even when such a call would be UB; the fact that we might have undefined behavior within a constant expression evaluation of the library function `std::assume_aligned` is explicitly permitted by [expr.const]p4 (after the long list of bullets):

> If e satisfies the constraints of a core constant expression, but evaluation of e would evaluate an operation that has undefined behavior as specified in Clause 15 through Clause 31 of this document[...], it is unspecified whether e is a core constant expression.

@chandlerc, @hfinkel: does an attribute-only implementation (with no constant evaluation enforcement) materially hurt the ability for the optimizer to use this annotation? Eg, in:

  extern char k[16];
  void f() {
    // the initializer of p is a constant expression and might get constant-folded to &k by the frontend
    char *p = std::assume_aligned<16>(&k);
    // ...do stuff...

the alignment assumption may well never be emitted as IR. Is that acceptable?



More information about the libcxx-commits mailing list