C++ MOVAPS访问未对齐的地址

C++ MOVAPS访问未对齐的地址,c++,visual-studio-2013,sse,memory-alignment,disassembly,C++,Visual Studio 2013,Sse,Memory Alignment,Disassembly,出于某种原因,我的一个函数正在使用未对齐的参数调用SSE指令movaps,这会导致崩溃。它发生在函数的第一行,其余的都需要在那里,以便崩溃发生,但为了清晰起见,需要将其放在这里 Vec3f CrashFoo( const Vec3f &aVec3, const float aFloat, const Vec2f &aVec2) { const Vec3f vecNew = Normalize(Vec3f(aVec3.x, aVec

出于某种原因,我的一个函数正在使用未对齐的参数调用SSE指令
movaps
,这会导致崩溃。它发生在函数的第一行,其余的都需要在那里,以便崩溃发生,但为了清晰起见,需要将其放在这里

Vec3f CrashFoo(
    const Vec3f &aVec3,
    const float  aFloat,
    const Vec2f &aVec2)
{
    const Vec3f vecNew =
        Normalize(Vec3f(aVec3.x, aVec3.x, std::max(aVec3.x, 0.0f)));

    // ...
}
以下是我在调试主界面中的调用方式:

int32_t main(int32_t argc, const char *argv[])
{
    Vec3f vec3{ 0.00628005248f, -0.999814332f, 0.0182171166f };
    Vec2f vec2{ 0.947231591f, 0.0522233732f };
    float floatVal{ 0.010f };

    Vec3f vecResult = CrashFoo(vec3, floatVal, vec2);

    return (int32_t)vecResult.x;
}
这是从
CrashFoo
函数开始到崩溃行的反汇编:

00007FF7A7DC34F0  mov         rax,rsp  
00007FF7A7DC34F3  mov         qword ptr [rax+10h],rbx  
00007FF7A7DC34F7  push        rdi  
00007FF7A7DC34F8  sub         rsp,80h  
00007FF7A7DC34FF  movaps      xmmword ptr [rax-18h],xmm6  
00007FF7A7DC3503  movss       xmm6,dword ptr [rdx]  
00007FF7A7DC3507  movaps      xmmword ptr [rax-28h],xmm7  
00007FF7A7DC350B  mov         dword ptr [rax+18h],0  
00007FF7A7DC3512  mov         rdi,r9  
00007FF7A7DC3515  mov         rbx,rcx  
00007FF7A7DC3518  movaps      xmmword ptr [rax-38h],xmm8  
00007FF7A7DC351D  movaps      xmmword ptr [rax-48h],xmm9  
00007FF7A7DC3522  movaps      xmmword ptr [rax-58h],xmm10  
00007FF7A7DC3527  lea         rax,[rax+18h]  
00007FF7A7DC352B  xorps       xmm8,xmm8  
00007FF7A7DC352F  comiss      xmm8,xmm6  
00007FF7A7DC3533  movaps      xmmword ptr [rax-68h],xmm11  
我的理解是,它首先执行通常的函数调用,然后通过将一些SSE寄存器(
xmm6
-
xmm11
)的当前内容保存到堆栈上开始准备,以便后续代码可以自由使用它们。
xmm*
寄存器一个接一个地存储到从
[rax-18h]
[rax-68h]
的地址中,由于
rax=0xe4d987f788
,这些寄存器很好地对齐到16个字节,但是在
xmm11
寄存器被存储之前,
rax
增加了18h,这破坏了对齐,导致崩溃。
xorps
comiss
行是实际代码开始的地方(
std::max
与0的比较)。当我删除std::max时,它工作得很好

你认为这种行为有什么原因吗

附加信息 我上传了一个在我的VisualStudio中崩溃的文件,但不是在IDEone中

代码在Visual Studio 2013更新5(x64版本,v120)中编译。我已经将项目的“Struct Member Alignment”设置设置为16字节,但是没有什么改进,并且在我使用的结构中没有打包
pragma
。错误消息是:

PG3Render.exe中0x00007ff7a7dc3533处的首次机会异常:0xC0000005:访问冲突读取位置0xFFFFFFFFFF


gcc和clang都很好,可以为您的示例生成非崩溃的非矢量化代码。(当然,我是为LinuxSysvABI编译的,其中没有任何向量reg被保存,因此它们没有生成代码来保存堆栈上的xmm{6..15}。)

你的IDEone链接也没有显示崩溃,所以IDK。我有在线编译和运行的网站,有MSVC作为一个选项。如果您的程序使用
system
在自身上运行反汇编程序,您甚至可以从中获得asm:P


对于
rax
,您发布的asm输出肯定会崩溃:

00007FF7A7DC3522  movaps      xmmword ptr [rax-58h],xmm10  
00007FF7A7DC3527  lea         rax,[rax+18h]  
...
00007FF7A7DC3533  movaps      xmmword ptr [rax-68h],xmm11
考虑到LEA,第二个存储地址是
[init_rax-50h]
,与以前的存储地址仅相差8B。一个或另一个会出错这似乎是您应该报告的编译器错误。


我不知道为什么您的编译器会使用
lea
而不是
addrax,18h
。它会在碰撞过程中用comiss(
comiss)

敲击标志之前执行此操作?rax=000000 CFD296E900 RBX=000000 CFD296E960 RCX=000000 CFD296E960 RDX=000000 CFD296EA90 RSI=000000 CFD2B5C400 RDI=000000 CFD296E910 R8=000000 CFD296EA98 R9=000000 CFD296E910 R10=B5026F5AA96619R11=000000 286R12=0000000000000000 R13=FFFFFFFFFFFFFF R14=0000000000000000 R15=0000000000000000000000 RIP=00007FF6CF2D50BD RSP=000000 CFD296E860 RBP=000000 CFD296E9F0 EFL=00010203 000000 CFD296E8A8=。当使用SSE对齐指令[如MOVAPS]时,您的数据结构和类型需要对齐到16字节。是的,它使用RAX-58作为地址。由于RAX的下半部分是900,它将生成958,该地址未与16字节地址对齐,它将由于未对齐的访问而导致GP故障。如果编译器没有意识到这一点,并且生成代码“认为”它可以工作,那就太糟糕了。您确定源代码中没有对齐编译标志或“pack pragma”吗?根据@Mats的发现,这些指令(特别是在Vec3Base/Vec3f上)可能会导致问题-这里写得不错:我的一个朋友在MSVC 2015上为我测试了这个示例,它编译并运行良好,所以它看起来确实是MSVC 2013中的一个bug。然而,我怀疑向微软报告它是否有意义,因为它与旧产品有关,而且他们有时甚至在当前版本(而不是旧版本)内也不会修复编译器错误。幸运的是,我发现使用AVX而不是SSE是一个可行的解决办法。