InsideLoop
InsideLoop

Reputation: 6255

Align a pointer

I want to align a pointer p so that p = a (modulo b). Most of the time, a = 0 and b = 32 or 64 for SIMD alignement, but I might also want a = 64 and b = 128 when I want to fine tune my algorithm for cache associativity.

My best solution so far is to cast the pointer to a std::uintptr_t and use modulo operations on integers to move the pointer. Unfortunately, it is not portable as casting a pointer to std::uintptr_t is not "allowed". But it works on all platforms I have tried so far.

On what kind of platform would such a code break?

Upvotes: 4

Views: 1715

Answers (3)

Michael C. Lehn
Michael C. Lehn

Reputation: 3194

The following C++ code requires that alignment is a power of 2. Memory allocated wit malloc_aligned must be released with free_aligned:

void *
malloc_aligned(std::size_t alignment, std::size_t size)
{
    alignment = std::max(alignment, alignof(void *));
    size     += alignment;

    void *ptr  = std::malloc(size);
    void *ptr2 = (void *)(((uintptr_t)ptr + alignment) & ~(alignment-1));
    void **vp  = (void**) ptr2 - 1;
    *vp        = ptr;
    return ptr2;
}

void
free_aligned(void *ptr)
{
    std::free(*((void**)ptr-1));
}

Upvotes: 1

eerorika
eerorika

Reputation: 238481

std::align does not accept any power of 2 for alignement. I will be fixed in C++17, but it is useless right now

Let's see what the standard (draft) says.

[ptr.align]

2 Requires:

(2.1) — alignment shall be a fundamental alignment value or an extended alignment value supported by the implementation in this context.

So, any fundamental alignment value is allowed, and possibly others if allowed by the implementation. Let's see what a fundamental alignment is.

[basic.align]

2 A fundamental alignment is represented by an alignment less than or equal to the greatest alignment supported by the implementation in all contexts, which is equal to alignof(std::max_align_t)...

Ok. Then there is a limitation to all alignments.

4 Alignments are represented as values of the type std::size_t. Valid alignments include only those values returned by an alignof expression for the fundamental types plus an additional implementation-defined set of values, which may be empty. Every alignment value shall be a non-negative integral power of two.

So, not only are power of 2 alignments allowed, but in fact only powers of 2 alignments are allowed. The difference with C++17 is that until then, only alignments less than or equal to alignof(std::max_align_t) (i.e. fundamental alignments) are allowed. Support for bigger alignment depends on implementation.

TL;DR Your premise is wrong. But whether std::align works, is still implementation defined until C++17.

Upvotes: 0

Maxim
Maxim

Reputation: 33

Might be you look for something like this:

inline size_t AlignHi(size_t size, size_t align)
{
    return (size + align - 1) & ~(align - 1);
}

inline void * Allocate(size_t size, size_t align)
{
#if defined(_MSC_VER) 
    return _aligned_malloc(size, align);
#elif defined(__GNUC__)
    align = AlignHi(align, sizeof(void*));
    size = AlignHi(size, align);
    void * ptr;
    int result = ::posix_memalign(&ptr, align, size);
    return result ? NULL : ptr;
#else
    return malloc(size);
#endif
}

inline void Free(void * p)
{
#if defined(_MSC_VER) 
    _aligned_free(p);
#else
    free(p);
#endif
}

Upvotes: 2

Related Questions