1
votes

I am writing a memory manager for microcontrollers that uses a uint8_t array for the pool. From this pool, it allocates memory of the requested size to the user.

I am looking at other memory implementations. Contiki has one named mmem. In their documentation they state:

It must be noted that the memory allocated with mmem_alloc() is 1-byte aligned. This is different from what malloc() does. The memory allocated with malloc() is suitably aligned for every data type and the returned void pointer can be safely casted to any other pointer type.

Instead, the pointer to the memory allocated by mmem_alloc() cannot be safely converted to any pointer type other than char*, signed char* or unsigned char*.

This means that if the allocated memory chunk is used to store the contents of a struct type, either the struct must be declared packed or memcpy() must be used. With GCC a packed struct can be specified using the following syntax:

...

So this is a pretty big gotcha that I was never aware of.

A couple of questions:

  • Is this true for all architectures? I am reading on Wikipedia and they state that most archetectures support unaligned data, it just slows things down.

    (For x68): However, there are also instructions for unaligned access such as MOVDQU.

  • The Contiki link talks about packing the structs as a way to get around the alignment problem. Is there a way to "pack" a uint16_t or int32_t value? I provide macros to the user for common data access, so this could be a possible solution

Do ARM processors support automatic unaligned access? How about x86? Are there really this many gotchas?

Should I just only allow aligned allocation requests, and fill in whatever other data? This would be very annoying for me.

Edit:

Thank you for the helpful answers, however I cannot write something where the user requests alignment because of how the memory manager works (it has to defragment by moving data -- I am not willing to add the complexity of worrying about what alignment some data is, as that will significantly effect the performance of the whole system)

It seems that on both my system (Ubuntu) and the ARM core I am targeting, data alignment is not an issue.

From here:

-munaligned-access -mno-unaligned-access Enables (or disables) reading and writing of 16- and 32- bit values from addresses that are not 16- or 32- bit aligned. By default unaligned access is disabled for all pre-ARMv6 and all ARMv6-M architectures, and enabled for all other architectures.

...

Also, the following code does as you would want it to do on my (x86_64 Ubuntu 14.04) system:

#include "stdio.h"

int main(){
    char data[100];
    unsigned int *value;
    // Some random data
    unsigned int check = (unsigned int)0x324FE23A;

    // make the pointer unaligned
    value = (unsigned int *)(data + 1);
    *value = check;
    printf("bool=%u, value=%x, check=%x\n", *value==check, *value, check);

    return 0;
}

when I compile and run it:

$ cc  playground  cc align.c && ./a.out
bool=1, value=324fe23a, check=324fe23a

Solution:

I will add a precompiler flag to request that requests are fulfilled in words so that all data is automatically aligned. However, for the systems I am currently targeting this should not be necessary.

Outstanding Question:

If anybody knows a way to do:

#define tm_uint16_p(index)   ((uint16_t *)tm_void_p(index))

In a way that it will never matter if tm_void_p(index) is aligned then that would be great.

Note: the above just converts an index into a void pointer, then casts it as a uint16_t pointer. Obviously on some systems this will fail due to alignment issues -- is their a way to specify to the compiler to just deal with it?

2
Most of your questions will be answered here: What is the purpose of memory alignment?. Also watch out for aliasing problems.M.M
Even if the alignment is not necessary (i.e. the code won't crash without alignment), it usually performs slower than code with correct alignment.fuz
Thanks for the comment FUZxxl! That is absolutely a good point. Speed is not the number 1 concern for this application, but I will allow the user to specify "only aligned data" if they have this concern.vitiral

2 Answers

2
votes

It's not true for all processors.

If I were designing the low level API, on a processor where this mattered, I'd do something like void *my_alloc(size_t size, size_t align)

If callers are manipulating character arrays, they pass an align of 1. For various small types they pass sizeof(short), sizeof(int) etc.

Then give them an allocator for the clueless, where align is MIN(roundup_to_power_of_2(size), SIZE_ON_THIS_ARCH_THATS_ALWAYS_GOOD_ENOUGH)

And no, round_up_to_power_of_2 isn't a standard function (or macro) ;-)

0
votes

an easy solution is to pass the desired alignment as a second parameter to the function you write that will obtain the pointer to allocated memory