GCC向量扩展中的未对齐加载/存储

3

我需要使用GCC向量扩展访问未对齐的值

下面的程序会崩溃 - 在clang和gcc中都是如此

typedef int __attribute__((vector_size(16))) int4;
typedef int __attribute__((vector_size(16),aligned(4))) *int4p;

int main()
{
        int v[64] __attribute__((aligned(16))) = {};
        int4p ptr = reinterpret_cast<int4p>(&v[7]);
        int4 val = *ptr;
}

然而,如果我更改

typedef int __attribute__((vector_size(16),aligned(4))) *int4p;

to

typedef int __attribute__((vector_size(16),aligned(4))) int4u;
typedef int4u *int4up;

生成的汇编代码是正确的(使用不对齐的加载方式)- 在clang和gcc中都是如此。

单一定义有什么问题或者我错过了什么?这可能是clang和gcc中相同的错误吗?

注意:这在clang和gcc中都发生。


如果您将typedef分为两步,一步是针对非对齐向量,另一步是指向该向量的指针,则可以正常工作。当您像这样直接进行时,对齐属性会以某种方式被删除。请提交错误报告。 - Marc Glisse
@MarcGlisse 你是指两步定义typedef的方式吗?请问具体是怎样的呢? - Artyom
@MarcGlisse 我明白你的意思,但我发现无论是clang还是gcc,它们的行为都很相似。 - Artyom
此时我真的认为Bugzilla是您获得澄清(和希望的修复)的最佳机会。请注意,如果您还没有帐户,则需要向管理员发送电子邮件(如帐户创建页面上所述)以获取一个。 - Marc Glisse
@MarcGlisse Bug,为什么在clang和gcc中出现了相同的问题? - Artyom
1个回答

7

TL;DR

你已经改变了指针类型本身的对齐方式,而不是指针所指向的类型。这与 vector_size 属性无关,与 aligned 属性有关。这也不是一个 bug,在 GCC 和 Clang 中都实现正确。

Long Story

来自 GCC 文档§ 6.33.1 Common Type Attributes(强调添加)

alignedalignment

此属性为 指定类型的变量 指定最小对齐(以字节为单位)。[...]

所讨论的类型是 正在声明的类型,而不是 正在声明的类型指向的类型。因此,

typedef int __attribute__((vector_size(16),aligned(4))) *int4p;

声明一个指向类型为*T的对象的新类型T,其中:
  • *T是一个16字节向量,默认对齐其大小(16字节)
  • T是一个指针类型,此类型的变量可以被异常地存储在低至4字节的边界上(即使它们所指向的是一个更加对齐的类型*T)。

与此同时,§ 6.49通过内置函数使用矢量指令(重点添加)

On some targets, the instruction set contains SIMD vector instructions which operate on multiple values contained in one large register at the same time. For example, on the x86 the MMX, 3DNow! and SSE extensions can be used this way.

The first step in using these extensions is to provide the necessary data types. This should be done using an appropriate typedef:

typedef int v4si __attribute__ ((vector_size (16)));

The int type specifies the base type, while the attribute specifies the vector size for the variable, measured in bytes. For example, the declaration above causes the compiler to set the mode for the v4si type to be 16 bytes wide and divided into int sized units. For a 32-bit int this means a vector of 4 units of 4 bytes, and the corresponding mode of foo is V4SI.

The vector_size attribute is only applicable to integral and float scalars, although arrays, pointers, and function return values are allowed in conjunction with this construct. Only sizes that are a power of two are currently allowed.

演示

#include <stdio.h>

typedef int __attribute__((aligned(128))) * batcrazyptr;
struct batcrazystruct{
    batcrazyptr ptr;
};

int main()
{
    printf("Ptr:    %zu\n", sizeof(batcrazyptr));
    printf("Struct: %zu\n", sizeof(batcrazystruct));
}

输出:

Ptr:    8
Struct: 128

这与batcrazyptr ptr本身的对齐需求更改相一致,而不是其指针对象,并与文档内容一致。

解决方案

恐怕您将被迫使用一系列typedef,就像您使用int4u一样。为每个指针级别指定对齐属性是不合理的。


__attribute__(...)的位置决定它适用于哪个(可能是内部的)类型,这将非常有意义... - Marc Glisse
1
当前行为 typedef int*P;typedef P v __attribute__((vector_size(16))); 创建指向向量的指针而不是指针向量,实在是非常不自然。 - Marc Glisse
@MarcGlisse 不要引用我说的话,但是如果我没记错,const/volatile 只用了两个位标志。我相当确定许多属性需要更多的空间……无论如何,GCC 和 Clang 的 __attribute__ 系统已经存在太长时间了,而且没有强制规定该关键字在类型声明中的特定位置。因此,现在有太多的代码会破坏,如果突然执行严格的规定。 - Iwillnotexist Idonotexist
1
属性确实需要比const更多的存储空间(尽管许多属性是特殊的,特别是vector_size和可能的aligned),但我认为我们正在讨论使用哪种语法来实现给定类型(该类型具有某些内部类型的属性),因此最终类型已经确定并占用它所需的空间。我同意语法不太可能改变 :-( - Marc Glisse
@Artyom,你不需要立即给我赏金。你已经花费了相当多的声望来设置悬赏,这不仅为你带来了一个答案,还有7天的广泛曝光。你可以通过保留悬赏并从流量中收集额外的声望来赚回更多你花费的声望。 - Iwillnotexist Idonotexist
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接