Bug 55727 - better support for dynamic allocation of over-aligned types
Summary: better support for dynamic allocation of over-aligned types
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 4.8.0
: P3 normal
Target Milestone: 7.0
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-12-18 08:52 UTC by Matthias Kretz (Vir)
Modified: 2016-12-07 15:59 UTC (History)
2 users (show)

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


Attachments
simple testcase for new (157 bytes, text/x-c++src)
2012-12-18 08:52 UTC, Matthias Kretz (Vir)
Details
simple testcase for std::vector (163 bytes, text/x-c++src)
2012-12-18 08:53 UTC, Matthias Kretz (Vir)
Details
support for over-aligned types in new_allocator (680 bytes, patch)
2012-12-18 18:20 UTC, Matthias Kretz (Vir)
Details | Diff
updated the patch (725 bytes, patch)
2012-12-18 18:30 UTC, Matthias Kretz (Vir)
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Matthias Kretz (Vir) 2012-12-18 08:52:42 UTC
Created attachment 28991 [details]
simple testcase for new

Hello,

I just tested the alignas support in GCC 4.8 and wondered how far the C++11 spec defines behavior of dynamic allocation of over-aligned types. Because, with GCC 4.8, dynamic allocation via new or std::allocator (e.g. in std::vector) results in unaligned placement. I quote the relevant parts of the spec below, because I believe GCC could be much more helpful to developers.

I believe a must-have is that if dynamic allocation of an over-aligned type would result in misalignment, the allocatio fails (3.11.9, 17.6.3.5.6). Perfect would be a compile-time failure, which is theoretically possible. Runtime errors resulting from misaligned allocation are often hard to debug, therefore it would be *very* helpful to get an early warning about misalignment.

As I understand it, the C++ spec allows for GCC to also just do the right thing automatically, which would be my preferred solution. So when I'd do:
struct alignas(128) Foo { int x; };
Foo *f = new Foo;
I'd (per default) get a pointer f that is aligned as requested (without having to overload new/delete). The same would be very important for std::vector<Foo>. Right now it does not even suffice to reimplement new/delete inside Foo to make std::vector<Foo> work.

C++11 spec quotes:

3.9.2.3 "Pointers to over-aligned types (3.11) have no special representation, but their range of valid values is restricted by the extended alignment requirement. This International Standard specifies only two ways of obtaining such a pointer: taking the address of a valid object with an over-aligned type, and using one of the runtime pointer alignment functions. An implementation may provide other means of obtaining a valid pointer value for an over-aligned type.

3.11.9 "a request for runtime allocation of dynamic storage for which the requested alignment cannot be honored shall be treated as an allocation failure"

5.3.4.1 "It is implementation-defined whether over-aligned types are supported (3.11)."

5.3.4.14 "The block of storage is assumed to be appropriately aligned and of the requested size."

17.6.3.5.6 "If the alignment associated with a specific over-aligned type is not supported by an allocator, instantiation of the allocator for that type may fail. The allocator also may silently ignore the requested alignment. [ Note: Additionally, the member function allocate for that type may fail by throwing an object of type std::bad_alloc. — end note"

20.6.9.1.5 "Returns: A pointer to the initial element of an array of storage of size n * sizeof(T), aligned appropriately for objects of type T. It is implementation-defined whether over-aligned types are supported (3.11)."
Comment 1 Matthias Kretz (Vir) 2012-12-18 08:53:24 UTC
Created attachment 28992 [details]
simple testcase for std::vector
Comment 2 Matthias Kretz (Vir) 2012-12-18 09:11:41 UTC
(In reply to comment #0)
> Right now it does not even suffice to reimplement new/delete inside Foo to make
> std::vector<Foo> work.

Sorry, this statement seems to be wrong. The issue that I actually have is of the form:

from my library:
  struct alignas(32) A { ... }; //overloads new/delete for correct alignment

different library:
  template<typename T> struct B { T x; };

application code:
  std::vector<B<A>>

The members of the vector are now misaligned because obviously the new/delete overloads in A are not used. But the alignment information from A is passed on to B so it could be used in the allocator of std::vector.
Comment 3 Matthias Kretz (Vir) 2012-12-18 13:26:21 UTC
(In reply to comment #2)
> (In reply to comment #0)
> > Right now it does not even suffice to reimplement new/delete inside Foo to make
> > std::vector<Foo> work.
> 
> Sorry, this statement seems to be wrong.

After more investigation I see that my original statement was right. The default allocator uses ::operator new(size_t) instead of T::operator new - and obviously it has to use the global one because allocation and construction are split into two functions.

I just created an allocator class that uses posix_memalign and __alignof to implement an allocator as I'd like to have for a proof of concept.

If you are interested, I can patch the new_allocator class to support over-alignment (while still using operator new). Would you be interested?
Comment 4 Matthias Kretz (Vir) 2012-12-18 18:20:00 UTC
Created attachment 29002 [details]
support for over-aligned types in new_allocator

I finished my allocator to fix the issue and it was easy to port it over to new_allocator. So here's the patch.

Questions:
1. I use sizeof(void*) for the natural alignment or alignof(std::max_align_t) for C++11. Is there a better way to do this?
2. I'm not sure you like the way I abuse enum for compile-time constants. May I use constexpr, or would that have to be ifdefed?
3. What's the proper integer type to use for pointer manipulation? I'd really like to write __p &= ~__alignment_mask, but I don't see how to not breaking strict-aliasing with this.

Note that new_allocator will compile to the same code as long as alignof(_Tp1) is <= alignof(std::max_align_t). The extra code to handle alignment will only kick in for over-aligned types - and those want the extra complexity.
Comment 5 Matthias Kretz (Vir) 2012-12-18 18:30:43 UTC
Created attachment 29003 [details]
updated the patch
Comment 6 Daniel Krügler 2012-12-19 11:56:32 UTC
(In reply to comment #0)
I suggest to also read the proposal

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3396.htm

It points out some general problems with allocation of over-aligned data as of the current specification and suggests a language extension for better support.
Comment 7 Jonathan Wakely 2016-12-07 15:59:36 UTC
(In reply to Matthias Kretz from comment #1)
> Created attachment 28992 [details]
> simple testcase for std::vector

This works as expected on trunk, now that we support aligned allocation.