定义具有特定对齐方式的自动变量的优雅方式
Elegant way to define an automatic variable with specific alignment
我正在使用 ARM 编译器并有一个硬件外围设备(具有直接内存访问),它需要对传递给它的内存缓冲区进行特定对齐(32 字节对齐)。当缓冲区为 global/static 并且可以使用编译器支持的 aligned
属性定义时,这不是问题。每当需要在本地传递某个函数中定义的缓冲区时,就会出现问题,即具有自动存储 class。我尝试做类似于以下的事情:
typedef struct __attribute__((aligned(32)))
{
char bytes[32];
} aligned_t;
_Static_assert(sizeof(aligned_t)==32, "Bad size");
void foo(void)
{
aligned_t alignedArray[NEEDED_SIZE/sizeof(aligned_t)];
//.... use alignedArray
}
这是愉快地编译并在 x86 编译器上工作。但不在 armcc 中,它在抱怨:
Warning: #1041-D: alignment for an auto object may not be larger than
8
所以这个方法是行不通的。还有一个,我觉得很丑:
void foo(void)
{
char unalignedBuffer[NEEDED_SIZE + 32 - 1];
char pAlignedBuffer = ALIGN_UP_32(unalignedBuffer);
//.... use pAlignedBuffer
}
而 ALIGN_UP_32
是 return unalignedBuffer
中第一个对齐地址的宏(我猜实现细节在这里并不重要)。
正如我所说,我不喜欢这种方法,想知道是否有更优雅的方法来实现同样的目的?
I am working with the ARM compiler
您是否也尝试过 最近的 GCC(可能配置为交叉编译器),例如2018 年 11 月的 GCC 8?
ARM ABI 不保证堆栈指针(可能)与 32 字节对齐。
所以任何自动变量都没有按照你想要的那样对齐。
您可以避免它们(并系统地使用适当对齐的堆内存区域)。或者你可以分配比需要更多的东西并对其进行指针运算。
我觉得你的 char* pAlignedBuffer = ALIGN_UP_32(unalignedBuffer);
是一个很好的方法,我相信优化编译器会生成非常高效的代码。
I don't like this approach and wondering if there is a more elegant way to achieve the same?
我相信你的方法很好,其他任何方法都是等效的。
PS。另一种方法可能是修补您的 GCC 编译器(可能使用插件)以更改堆栈指针的默认对齐方式(从而有效地更改您的 ABI and calling conventions)。这将花费您数周(或数月)的努力。
你的两个选项看起来是最简单的。然而(并且只是猜测,我没有想太多关于我自己的答案),另一种选择可能是创建另一个堆栈。当包含您的缓冲区的函数被执行时,上下文被切换(好吧,只是 SP - 在管理员模式下 - )现在 SP 指向第二个堆栈。这个堆栈分配在一个 32 位对齐的部分,它只会包含 32 位对齐的对象,所以当一个本地 32 位对齐的变量被创建时,它将被分配在一个 32 位对齐的内存堆中,一旦变量超出范围就会被释放.一旦函数执行完毕,SP 就会切换回主堆栈。函数的执行必须被视为临界区,以避免 push/pop 在错误的堆栈中。
我不认为这会产生堆栈溢出,但正如我所说,我离题了,以防万一它有帮助...
我正在使用 ARM 编译器并有一个硬件外围设备(具有直接内存访问),它需要对传递给它的内存缓冲区进行特定对齐(32 字节对齐)。当缓冲区为 global/static 并且可以使用编译器支持的 aligned
属性定义时,这不是问题。每当需要在本地传递某个函数中定义的缓冲区时,就会出现问题,即具有自动存储 class。我尝试做类似于以下的事情:
typedef struct __attribute__((aligned(32)))
{
char bytes[32];
} aligned_t;
_Static_assert(sizeof(aligned_t)==32, "Bad size");
void foo(void)
{
aligned_t alignedArray[NEEDED_SIZE/sizeof(aligned_t)];
//.... use alignedArray
}
这是愉快地编译并在 x86 编译器上工作。但不在 armcc 中,它在抱怨:
Warning: #1041-D: alignment for an auto object may not be larger than 8
所以这个方法是行不通的。还有一个,我觉得很丑:
void foo(void)
{
char unalignedBuffer[NEEDED_SIZE + 32 - 1];
char pAlignedBuffer = ALIGN_UP_32(unalignedBuffer);
//.... use pAlignedBuffer
}
而 ALIGN_UP_32
是 return unalignedBuffer
中第一个对齐地址的宏(我猜实现细节在这里并不重要)。
正如我所说,我不喜欢这种方法,想知道是否有更优雅的方法来实现同样的目的?
I am working with the ARM compiler
您是否也尝试过 最近的 GCC(可能配置为交叉编译器),例如2018 年 11 月的 GCC 8?
ARM ABI 不保证堆栈指针(可能)与 32 字节对齐。
所以任何自动变量都没有按照你想要的那样对齐。
您可以避免它们(并系统地使用适当对齐的堆内存区域)。或者你可以分配比需要更多的东西并对其进行指针运算。
我觉得你的 char* pAlignedBuffer = ALIGN_UP_32(unalignedBuffer);
是一个很好的方法,我相信优化编译器会生成非常高效的代码。
I don't like this approach and wondering if there is a more elegant way to achieve the same?
我相信你的方法很好,其他任何方法都是等效的。
PS。另一种方法可能是修补您的 GCC 编译器(可能使用插件)以更改堆栈指针的默认对齐方式(从而有效地更改您的 ABI and calling conventions)。这将花费您数周(或数月)的努力。
你的两个选项看起来是最简单的。然而(并且只是猜测,我没有想太多关于我自己的答案),另一种选择可能是创建另一个堆栈。当包含您的缓冲区的函数被执行时,上下文被切换(好吧,只是 SP - 在管理员模式下 - )现在 SP 指向第二个堆栈。这个堆栈分配在一个 32 位对齐的部分,它只会包含 32 位对齐的对象,所以当一个本地 32 位对齐的变量被创建时,它将被分配在一个 32 位对齐的内存堆中,一旦变量超出范围就会被释放.一旦函数执行完毕,SP 就会切换回主堆栈。函数的执行必须被视为临界区,以避免 push/pop 在错误的堆栈中。 我不认为这会产生堆栈溢出,但正如我所说,我离题了,以防万一它有帮助...