Bug 98641 - Feature request: implement pointer alignment builtins
Summary: Feature request: implement pointer alignment builtins
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 11.0
: P3 enhancement
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-01-12 17:01 UTC by ktkachov
Modified: 2021-01-13 14:31 UTC (History)
6 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description ktkachov 2021-01-12 17:01:49 UTC
We received reports that users found the pointer alignment builtins provided by LLVM useful in avoiding error-prone casting to and from intptr_t:
https://clang.llvm.org/docs/LanguageExtensions.html#alignment-builtins

It would be great if we could support them in GCC as well.

This would involve implementing:
Type __builtin_align_up(Type value, size_t alignment);
Type __builtin_align_down(Type value, size_t alignment);
bool __builtin_is_aligned(Type value, size_t alignment);

Using these builtins the compiler can also preserve pointer provenance information  more easily.
Comment 1 ktkachov 2021-01-12 17:02:40 UTC
The component is marked as C++, but it would be good to have these in C as well.
Comment 2 Jonathan Wakely 2021-01-13 10:07:16 UTC
We could make immediate use of these in libstdc++. We have std::align and std::assume_aligned in include/bits/align.h, and aligned_ceil in src/c++17/memory_resource.cc, and a couple of places in libsupc++/new_opa.cc and maybe other places too.
Comment 3 Jakub Jelinek 2021-01-13 10:18:22 UTC
assume_aligned is something different, I guess __builtin_is_aligned expands to an actual runtime check (perhaps optimized away if the pointer can't be misaligned).
So I guess __builtin_is_aligned (__builtin_assume_aligned (ptr, 32), 16) should optimize to true...

I guess I have big questions on what exactly will it do during constexpr evaluation - if it is on a pointer to object with certain alignment, for smaller alignment arguments guess it is just like pointer arithmetics then, but if it asks for larger alignment, shall it make it non-constant expression?
Comment 4 Jonathan Wakely 2021-01-13 10:21:41 UTC
(In reply to Jakub Jelinek from comment #3)
> assume_aligned is something different, I guess __builtin_is_aligned expands
> to an actual runtime check (perhaps optimized away if the pointer can't be
> misaligned).

But we could do this:

--- a/libstdc++-v3/include/bits/align.h
+++ b/libstdc++-v3/include/bits/align.h
@@ -98,7 +98,7 @@ align(size_t __align, size_t __size, void*& __ptr, size_t& __space) noexcept
        {
          // This function is expected to be used in hot code, where
          // __glibcxx_assert would add unwanted overhead.
-         _GLIBCXX_DEBUG_ASSERT((uintptr_t)__ptr % _Align == 0);
+         _GLIBCXX_DEBUG_ASSERT(__builtin_is_aligned(__ptr, _Align));
          return static_cast<_Tp*>(__builtin_assume_aligned(__ptr, _Align));
        }
     }
Comment 5 Alex Richardson 2021-01-13 12:44:05 UTC
In clang __builtin_assume_aligned will not be used by the constant expression evaluator, but it will be used for code generation: for example,

_Bool check(void* x) {
    return __builtin_is_aligned(__builtin_assume_aligned(x, 16), 16);
}

is folded to "return true", but something like

extern int i;
_Static_assert(__builtin_is_aligned(__builtin_assume_aligned(&i, 16), 8), "");
generates an "alignment of the base pointee object (4 bytes) is less than the asserted 16 byte" error when evaluating __builtin_assume_aligned().

https://godbolt.org/z/96h6j1
Comment 6 Alex Richardson 2021-01-13 12:53:32 UTC
(In reply to Jakub Jelinek from comment #3)
> assume_aligned is something different, I guess __builtin_is_aligned expands
> to an actual runtime check (perhaps optimized away if the pointer can't be
> misaligned).
> So I guess __builtin_is_aligned (__builtin_assume_aligned (ptr, 32), 16)
> should optimize to true...
> 
> I guess I have big questions on what exactly will it do during constexpr
> evaluation - if it is on a pointer to object with certain alignment, for
> smaller alignment arguments guess it is just like pointer arithmetics then,
> but if it asks for larger alignment, shall it make it non-constant
> expression?

Regarding your second question: If folding is possible during constant evaluation, clang will fold it even at -O0. If it's unknown, the LLVM IR optimization passes will attempt to fold it (taking into account __builtin_assume_aligned metadata if present).

For example, in:

static inline _Bool is_aligned_wrapper(void* x) {
    return __builtin_is_aligned(x, 4);
}

_Bool check_unknown(void* x) {
    return is_aligned_wrapper(x);
}

_Bool check_known_4_byte_alignment(void) {
    int i;
    return is_aligned_wrapper(&i);
}

_Bool check_assume_4_byte_alignment(void* x) {
    return is_aligned_wrapper(__builtin_assume_aligned(x, 4));
}

The check_unknown() function will perform a run-time check, the other two simply return true.

https://godbolt.org/z/v4Gaaj
Comment 7 Jakub Jelinek 2021-01-13 12:54:18 UTC
(In reply to Alex Richardson from comment #5)
> extern int i;
> _Static_assert(__builtin_is_aligned(__builtin_assume_aligned(&i, 16), 8),
> "");
> generates an "alignment of the base pointee object (4 bytes) is less than
> the asserted 16 byte" error when evaluating __builtin_assume_aligned().

in constant expression contexts perhaps, though I'd certainly not word it that way, if it was evaluated at runtime, it could have been true if one is lucky
(or ensures it manually, e.g. by
int alignas(16) i;
at the variable definition).
Comment 8 Alex Richardson 2021-01-13 14:31:02 UTC
(In reply to Jakub Jelinek from comment #3)
> I guess I have big questions on what exactly will it do during constexpr
> evaluation - if it is on a pointer to object with certain alignment, for
> smaller alignment arguments guess it is just like pointer arithmetics then,
> but if it asks for larger alignment, shall it make it non-constant
> expression?

When I implemented this for clang, I made the expression no longer constant if __builtin_is_aligned() cannot be evaluated to true (since it might actually be true at run time).
Therefore the following gives a "non a constant expression error rather than returning false":

extern int x;
_Static_assert(__builtin_is_aligned(&x, 8), "");

See https://github.com/llvm/llvm-project/commit/8c387cbea76b169f1f8ecc7693797e96567ed896#diff-b69ce3cbe3c48b9fac72f4c22fe7bb425c8757b144a87c44347ff1522360460fR211