关于 c:intel 处理器上的未对齐访问存储

unaligned access store on intel processor

考虑下面的示例。它在标记行出现 gcc 5.4 的段错误时
我用 g++ -O3 -std=c++11 编译它。它在指令 movaps 处失败,我怀疑它执行未对齐的内存访问。可能是 gcc 为这样一个简单的示例生成了非法代码,还是我遗漏了一些东西?
我在 Intel i5-5200U 上运行它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <vector>
#include <memory>
#include <cstdint>

using namespace std;

__attribute__ ((noinline))
void SerializeTo(const vector<uint64_t>& v, uint8_t* dest) {
  for (size_t i = 0; i < v.size(); ++i) {
    *reinterpret_cast<uint64_t*>(dest) = v[i];  // Segfaults here.
    dest += sizeof(uint64_t);
  }
}

int main() {
 std::vector<uint64_t> d(64);

 unique_ptr<uint8_t[]> tmp(new uint8_t[1024]);

 SerializeTo(d, tmp.get() + 6);

 return 0;
}


您在数组中步进了 6 个字节,所以它现在未对齐。编译器无法知道它必须避免需要对齐的指令;这就是类型双关语是未定义行为的原因。


在 c 中合法地执行类型双关的方法很少。

魔术函数 std::memcpy 是这里选择的工具:

1
2
3
4
5
6
7
__attribute__ ((noinline))
void SerializeTo(const vector<uint64_t>& v, uint8_t* dest) {
  for (size_t i = 0; i < v.size(); ++i) {
      std::memcpy(dest, std::addressof(v[i]), sizeof(v[i]));
    dest += sizeof(uint64_t);
  }
}

结果输出与 -std=c++11 -O3 -march=native -Wall -pedantic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SerializeTo(std::vector<unsigned long, std::allocator<unsigned long> > const&, unsigned char*):   # @SerializeTo(std::vector<unsigned long, std::allocator<unsigned long> > const&, unsigned char*)
        mov     rax, qword ptr [rdi]
        cmp     qword ptr [rdi + 8], rax
        je      .LBB0_3
        xor     ecx, ecx
.LBB0_2:                                # =>This Inner Loop Header: Depth=1
        mov     rax, qword ptr [rax + 8*rcx]
        mov     qword ptr [rsi + 8*rcx], rax
        add     rcx, 1
        mov     rax, qword ptr [rdi]
        mov     rdx, qword ptr [rdi + 8]
        sub     rdx, rax
        sar     rdx, 3
        cmp     rcx, rdx
        jb      .LBB0_2
.LBB0_3:
        ret

https://godbolt.org/g/ReGA9N