[cfe-dev] operator new alignment assumptions

John McCall via cfe-dev cfe-dev at lists.llvm.org
Mon Apr 16 09:08:58 PDT 2018

> On Apr 16, 2018, at 4:50 AM, Stephan Bergmann via cfe-dev <cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>> wrote:
> On 16/02/17 13:34, Stephan Bergmann via cfe-dev wrote:
>> On 02/16/2017 02:34 AM, Richard Smith wrote:
>>> On 13 February 2017 at 06:55, Stephan Bergmann <sbergman at redhat.com <mailto:sbergman at redhat.com>
>>> <mailto:sbergman at redhat.com <mailto:sbergman at redhat.com>>> wrote:
>>>     *  At least with the wording from P0035R4, it is unclear to me what
>>>     the alignment requirements in
>>>         struct S { char a[25]; };
>>>         S * func() { return new S(); }
>>>     are.  When compiled at -O for x86_64-unknown-linux-gnu, Clang trunk
>>>     apparently assumes that the pointer returned from operator new is
>>>     16-byte aligned (writing to it with movaps).  But the requirements
>>>     for that operator new in [new.delete.single] merely states that it
>>>     "allocates storage suitably aligned to represent any object of that
>>>     size", and I don't think there is a type of size 25 with a stricter
>>>     alignment requirement than 1 for that target.
>>> Yes, you're right that this is at best unclear and at worst says that
>>> our assumption is not valid for allocations that are not a multiple of
>>> the allocator alignment. This is the subject of C++ core issue 2207
>>> (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2207 <http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2207>, but
>>> there's no information on the public version of the issues list yet);
>>> there generally seemed to be consensus in committee discussion that
>>> global non-array non-aligned operator new should provide sufficient
>>> storage for any object with a fundamental alignment that fits in the
>>> storage, not just for fundamental alignments that divide the allocated size.
>>> For instance, we want to guarantee that this sort of thing works:
>>> S *p = func();
>>> static_assert(sizeof(long double) <= 25);
>>> new (p->a) long double; // ok, 'a' is suitably aligned for 'long double'
>>> Is this assumption causing problems in practice?
>> Don't know of any.  I stumbled across all this when seeing that the Firebird DBMS has a global operator new replacement that generally does merely 8-byte alignment on x86-64.  But I haven't come across an issue related to this fits--vs.--matches-exactly size assumption.
> Revisiting this now, I think I /do/ see a problem in practice after all with Clang making too aggressive assumptions about the aligned-ness of storage returned by "plain" global operator new, at least with the Firebird 3.0 that is bundled by LibreOffice:
> Firebird replaces that "plain" global operator new, which gets called with an argument of 24, and recent Clang (on both Linux and macOS x86-64) uses movaps (requiring 16-byte aligned memory) on the result. (And the result happens to be only 8-byte aligned, causing a SEGV.)
> According to the x86-64 SysV ABI, a struct with a 16-byte alignment requirement would need to have a size that is a multiple of 16 bytes, so Firebird's operator new replacement should be OK returning merely 8-byte aligned storage for a request of 24 bytes.

I don't think this is a valid line of reasoning.  Users can call `operator new` themselves, and they are not required to call it with the `sizeof` some specific type.  It's a pretty common idiom to allocate a header (usually pointer-aligned) + a trailing array (aligned to whatever the element type requires), and in fact that's an ABI-mandated pattern in C++ (albeit with operator new[] — and since it only happens for types with destructors it's very unlikely to trigger on a byte-aligned element type).

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20180416/7500936e/attachment.html>

More information about the cfe-dev mailing list