我正在使用ARM编译器,并且具有硬件外围设备(具有直接内存访问权限),该外围设备要求传递给它的内存缓冲区具有特定的对齐方式(32字节对齐方式)。当缓冲区是全局/静态缓冲区并且可以使用编译器支持的aligned
属性定义时,这不是问题。每当需要本地传递某些函数中定义的缓冲区(即具有自动存储类)时,就会出现问题。我试图做类似以下的事情:
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 中,它在抱怨:
警告:#1041-D:自动对象的对齐方式不得大于 8
所以这种方法行不通。还有一个我觉得很丑:
void foo(void)
{
char unalignedBuffer[NEEDED_SIZE + 32 - 1];
char pAlignedBuffer = ALIGN_UP_32(unalignedBuffer);
//.... use pAlignedBuffer
}
ALIGN_UP_32
是一个宏,用于返回unalignedBuffer
中的第一个对齐地址(我想这里的实现细节并不重要)。
就像我说的那样,我不喜欢这种方法,并且想知道是否有更优雅的方法来实现这一目标?
答案 0 :(得分:2)
我正在使用ARM编译器
您是否还尝试了 recent GCC(可能配置为交叉编译器),例如GCC 8将于2018年11月发布?
ARM ABI无法保证堆栈指针(可能)对齐到32个字节。
因此,任何自动变量的对齐方式都不理想。
您可以避免使用它们(并系统地使用适当对齐的堆内存区域)。或者,您可以分配超出所需数量的资源,并对其进行指针算术运算。
我认为您的char* pAlignedBuffer = ALIGN_UP_32(unalignedBuffer);
是一个很好的方法,并且我相信优化的编译器会生成非常有效的代码。
我不喜欢这种方法,并且想知道是否有更优雅的方法来实现这一目标?
我相信您的方法是好的,其他任何方法都是等效的。
PS。另一种方法可能是修补GCC编译器(可能带有插件)以更改堆栈指针的默认对齐方式(因此有效地更改了ABI和calling conventions)。这将需要数周(或数月)的时间。
答案 1 :(得分:1)
您的两个选项看起来最简单。但是(只是猜测,我对自己的答案没有考虑太多),另一个选择可能是创建另一个堆栈。当执行包含缓冲区的函数时,将切换上下文(嗯,只有SP-在超级用户模式下-),现在SP指向第二个堆栈。该堆栈在32位对齐的节中分配,并且仅包含32位对齐的对象,因此,在创建本地32位对齐的变量时,它将在32位对齐的内存堆中分配,一旦该变量超出范围,该内存将释放。一旦执行了该功能,SP将切换回主堆栈。为了避免在错误的堆栈中压入/弹出,必须将功能的执行视为关键区域。 我不认为这会导致堆栈溢出,但是正如我所说的那样,以防万一它有帮助...