7

C++17 introduced Dynamic memory allocation for over-aligned data

Beside the existing std::max_align_t, the fundamental alignment, it added __STDCPP_DEFAULT_NEW_ALIGNMENT__ the minimal alignment that the operator new guarantees.

With MSVC2017 64bit compilation, these constants result in a std::max_align_t of size 8 and __STDCPP_DEFAULT_NEW_ALIGNMENT__ of size 16.

It is however allowed to overrule the operator new/free, as mentioned on cppreference: operator new - global replacements.

Looking at all of the documents, it's unclear to me if this function is allowed to provide a new default alignment and if so, if we are allowed to redefine this constant.

An example for illustration:

#include <new>
#include <iostream>
#include <cassert>
#include <cstdint>
#include <cstddef>

static_assert(alignof(std::max_align_t) == 8);
static_assert(__STDCPP_DEFAULT_NEW_ALIGNMENT__ == 16);


void * operator new(size_t size) 
{ 
    std::cout << "New operator overloading " << std::endl; 
    void * p = std::malloc((size == 8) ? 16 : size); 
    assert(std::uintptr_t(p)%16 == 0);
    if (size == 8)
        {
        auto correctedPtr = std::uintptr_t(p) + 8;
        return (void*)correctedPtr;
        }
    return p; 
} 

void operator delete(void * p) 
{ 
    std::cout << "Delete operator overloading " << std::endl; 
    if (std::uintptr_t(p)%16 != 0)
    {
        auto correctedPtr = std::uintptr_t(p) - 8;
        std::free((void*)correctedPtr);
    }
    std::free(p); 
}

namespace
{
    struct D
    {
        double d;
    };
}


int main(int, char**)
{
    new D{};
    return 0;
}

Code at compiler explorer

The reason I'm asking this, is because I'm investigating crashes in an MSVC program that is now being compiled with Clang. Here we noticed that clang uses CPU instructions that rely on this 16 bit alignment in order to initialize a class of size 8.

JVApen
  • 11,008
  • 5
  • 31
  • 67
  • You can provide a new default alignment. You cannot redefine the constant `__STDCPP_DEFAULT_NEW_ALIGNMENT__`. – L. F. Jun 22 '19 at 09:22
  • @L.F. If that's the case, why does my program currently crash? – JVApen Jun 22 '19 at 09:22
  • 1
    That's because, by providing a new default alignment, you break the system's alignment requirement. So the system can crash. – L. F. Jun 22 '19 at 09:23
  • 1
    I think you should be doing `-8` instead of `+8` in `delete` – Sopel Jun 22 '19 at 10:01
  • Thanks I've corrected it, however, its not the real code. That is much more complex, this was just for informational purpose – JVApen Jun 22 '19 at 10:03
  • You have another error in your code: your overload of `operator delete` calls `std::free` twice if `p%16!=0` – Handy999 Jun 23 '19 at 17:41
  • Since you write that this is not the real code: please provide a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) – Handy999 Jun 23 '19 at 17:42
  • @Handy999 The code crashed on the constructor of the class, I wasn't able to test the destruction part. My point about it not being 'real' code, is that this is a simplified version that doesn't have a functional sense – JVApen Jun 23 '19 at 17:47

1 Answers1

5

According to N4659 (last public draft for C++17):

6.7.4p3:

Any allocation and/or deallocation functions defined in a C++program, including the default versions in the library, shall conform to the semantics specified in 6.7.4.1 and 6.7.4.2.

6.7.4.1p2:

... The pointer returned shall be suitably aligned so that it can be converted to a pointer to any suitable complete object type (21.6.2.1) and then used to access the object or array in the storage allocated(until the storage is explicitly deallocated by a call to a corresponding deallocation function). ...

19.8p1:

The following macro names shall be defined by the implementation: ... __STDCPP_DEFAULT_NEW_ALIGNMENT__ An integer literal of type std::size_t whose value is the alignment guaranteed by a call to operator new(std::size_t) or operator new[](std::size_t). ...

19.8p4:

If any of the pre-defined macro names in this subclause, or the identifier defined, is the subject of a #define or a #undef preprocessing directive, the behavior is undefined. ...

So, you cannot change the __STDCPP_DEFAULT_NEW_ALIGNMENT__ value inside your program, and if your allocation function is called for alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) type of size 8, you cannot detect that, but you still need to return a suitably aligned pointer.

Nonetheless, you can change the __STDCPP_DEFAULT_NEW_ALIGNMENT__ value as defined by clang itself using -fnew-alignment compiler option. Not sure if it helps in your case.

Kit.
  • 2,386
  • 1
  • 12
  • 14
  • Update: `-XClang -fnew-alignment=8` as extra compilation flags seems to resolve the problem at hand – JVApen Jun 23 '19 at 13:31