读/写部分分配的对齐内存

时间:2016-12-14 21:59:36

标签: c++ memory language-lawyer sse memory-alignment

有关于访问未分配内存的问题很多,这显然是未定义的行为。但是下面的角落案例呢。

考虑以下结构,它与16个字节对齐,但只占用8个字节:

struct alignas(16) A
{
    float data[2]; // the remaining 8 bytes are unallocated
};

现在我们通过SSE对齐的加载/存储内在函数访问16个字节的数据:

__m128 test_load(const A &a)
{
    return _mm_load_ps(a.data);
}

void test_store(A &a, __m128 v)
{
    _mm_store_ps(a.data, v);
}

这也是未定义的行为,我应该使用填充吗?

无论如何,既然英特尔内在函数不是标准的C ++,那么在标准C ++中访问部分分配但对齐的内存块(不大于对齐的大小)是不确定的行为吗?

我解决了内在案例和标准C ++案例。我对他们两个都感兴趣。

2 个答案:

答案 0 :(得分:3)

另见Is it safe to read past the end of a buffer within the same page on x86 and x64?这个问题的阅读部分基本上与此重复。

根据ISO C ++标准,这是UB,但我认为这样的只读访问在提供英特尔内在函数的实现上安全地工作(即编译到你期望的asm)(可以自由定义任何额外的内容)他们想要的行为)。它在asm中绝对安全,但风险在于优化将UB转换为错误编译代码的C ++编译器如果能够证明没有任何东西可以读取,则可能会导致问题。关于相关问题,对此有一些讨论。

在对象之外写字总是不好。不要这样做,即使您放回了之前读过的相同垃圾:非原子加载/存储对可能是一个问题,具体取决于结构后面的数据。

唯一可行的是在一个数组中,你知道接下来会发生什么,并且有未使用的填充。例如使用由4B重叠的16B存储写出3 - float结构的数组。 (没有alignas用于过度对齐,因此数组将它们打包在一起而不填充)。

3 float s的结构比2 floats更好。

对于此特定示例(2个浮点数),您可以使用MOVSD执行64位零扩展加载,使用MOVSD或MOVLPS执行__m128的低半部分的64位存储

答案 1 :(得分:0)

语言律师对此的回答是“这个问题没有实际意义”。 _mm_load_ps未在标准中定义,它使用的ASM指令也未在标准中定义。 C ++没有解决这个问题。

至于你的第二个问题 - 从C ++访问未分配的内存这种方式显然是未定义的行为。此内存中没有放置任何对象,因此您无法访问它。