如何告诉GCC指针参数始终是双字对齐的?

ysa*_*sap 25 c gcc c99 alignment

在我的程序中,我有一个功能,可以添加一个简单的向量c[0:15] = a[0:15] + b[0:15].功能原型是:

void vecadd(float * restrict a, float * restrict b, float * restrict c);
Run Code Online (Sandbox Code Playgroud)

在我们的32位嵌入式架构中,有一个加载/存储双字加载/存储选项,如:

r16 = 0x4000  ;
strd r0,[r16] ; stores r0 in [0x4000] and r1 in [0x4004]
Run Code Online (Sandbox Code Playgroud)

GCC优化器识别循环的向量性质并生成代码的两个分支 - 一个用于3个数组是双字对齐的情况(因此它使用双重加载/存储指令)而另一个用于数组的情况是字对齐的(它使用单个加载/存储选项).

问题是地址对齐检查相对于加法部分是昂贵的,我想通过暗示编译器a,b和c始终是8对齐来消除它.是否有一个修饰符添加到指针声明以告诉编译器?

用于调用此函数的数组具有aligned(8)属性,但它不会反映在函数代码本身中.是否可以将此属性添加到函数参数?

ams*_*ams 10

如果属性不起作用,或者不是一个选项....

我不确定,但试试这个:

void vecadd (float * restrict a, float * restrict b, float * restrict c)
{
   a = __builtin_assume_aligned (a, 8);
   b = __builtin_assume_aligned (b, 8);
   c = __builtin_assume_aligned (c, 8);

   for ....
Run Code Online (Sandbox Code Playgroud)

这应该告诉GCC指针是对齐的.从那以后它是否符合您的要求取决于编译器是否可以有效地使用该信息; 它可能不够智能:这些优化并不容易.

另一种选择可能是将float包装在一个必须是8字节对齐的联合内:

typedef union {
  float f;
  long long dummy;
} aligned_float;

void vedadd (aligned_float * a, ......
Run Code Online (Sandbox Code Playgroud)

我认为这应该强制执行8字节对齐,但同样,我不知道编译器是否足够智能使用它.


ysa*_*sap 10

按照我在系统上找到的一段示例代码,我尝试了以下解决方案,其中包含了前面给出的一些答案的想法:基本上,创建一个64位类型的小型浮点数的联合 - 在这种情况下,浮点数的SIMD向量 - 并使用操作数浮点数组的强制类型调用函数:

typedef float f2 __attribute__((vector_size(8)));
typedef union { f2 v; float f[2]; } simdfu;

void vecadd(f2 * restrict a, f2 * restrict b, f2 * restrict c);

float a[16] __attribute__((aligned(8)));
float b[16] __attribute__((aligned(8)));
float c[16] __attribute__((aligned(8)));

int main()
{
    vecadd((f2 *) a, (f2 *) b, (f2 *) c);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

现在编译器不会生成4对齐分支.

然而,这__builtin_assume_aligned()将是一个更好的解决方案,防止演员和可能的副作用,如果它只有...

编辑:我注意到内置函数实际上是我们的实现错误(即,它不仅不起作用,但它会导致代码中的计算错误.


jww*_*jww 6

如何告诉GCC指针参数始终是双字对齐的?

看起来像GCC的新版本有__builtin_assume_aligned:

内置功能: void * __builtin_assume_aligned (const void *exp, size_t align, ...)

此函数返回其第一个参数,并允许编译器假定返回的指针至少对齐字节对齐.这个内置可以有两个或三个参数,如果它有三个,第三个参数应该有整数类型,如果它是非零意味着不对齐偏移.例如:

void *x = __builtin_assume_aligned (arg, 16);
Run Code Online (Sandbox Code Playgroud)

意味着编译器可以假设x(设置为arg)至少是16字节对齐,而:

void *x = __builtin_assume_aligned (arg, 32, 8);
Run Code Online (Sandbox Code Playgroud)

表示编译器可以假设为x,设置为arg,(char*)x - 8是32字节对齐的.

基于Stack Overflow大约2010年的一些其他问题和答案,看起来内置在GCC 3和早期GCC 4中不可用.但我不知道截止点在哪里.