定义具有特定对齐方式的自动变量的优雅方法

时间:2018-11-29 15:33:25

标签: c arm memory-alignment armcc

我正在使用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中的第一个对齐地址(我想这里的实现细节并不重要)。

就像我说的那样,我不喜欢这种方法,并且想知道是否有更优雅的方法来实现这一目标?

2 个答案:

答案 0 :(得分:2)

  

我正在使用ARM编译器

您是否还尝试了 recent GCC(可能配置为交叉编译器),例如GCC 8将于2018年11月发布?

ARM ABI无法保证堆栈指针(可能)对齐到32个字节。

因此,任何自动变量的对齐方式都不理想。

您可以避免使用它们(并系统地使用适当对齐的堆内存区域)。或者,您可以分配超出所需数量的资源,并对其进行指针算术运算。

我认为您的char* pAlignedBuffer = ALIGN_UP_32(unalignedBuffer);是一个很好的方法,并且我相信优化的编译器会生成非常有效的代码。

  

我不喜欢这种方法,并且想知道是否有更优雅的方法来实现这一目标?

我相信您的方法是好的,其他任何方法都是等效的。

PS。另一种方法可能是修补GCC编译器(可能带有插件)以更改堆栈指针的默认对齐方式(因此有效地更改了ABIcalling conventions)。这将需要数周(或数月)的时间。

答案 1 :(得分:1)

您的两个选项看起来最简单。但是(只是猜测,我对自己的答案没有考虑太多),另一个选择可能是创建另一个堆栈。当执行包含缓冲区的函数时,将切换上下文(嗯,只有SP-在超级用户模式下-),现在SP指向第二个堆栈。该堆栈在32位对齐的节中分配,并且仅包含32位对齐的对象,因此,在创建本地32位对齐的变量时,它将在32位对齐的内存堆中分配,一旦该变量超出范围,该内存将释放。一旦执行了该功能,SP将切换回主堆栈。为了避免在错误的堆栈中压入/弹出,必须将功能的执行视为关键区域。 我不认为这会导致堆栈溢出,但是正如我所说的那样,以防万一它有帮助...