MOVAPS 访问未对齐的地址
MOVAPS accesses unaligned address
出于某种原因,我的一个函数正在调用带有未对齐参数的 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
) 的当前内容保存到堆栈中来开始准备 playground,因此它们可以被后续代码自由使用。 xmm*
寄存器一个接一个地存储到从 [rax-18h]
到 [rax-68h]
的地址,这些地址从 rax=0xe4d987f788
开始很好地对齐到 16 个字节,但在 xmm11
寄存器之前被存储后,rax
增加了 18h,这打破了导致崩溃的对齐方式。 xorps
和 comiss
行是实际代码开始的地方(std::max
与 0 的比较)。当我删除 std::max
时,它运行良好。
您认为这种行为有什么原因吗?
附加信息
我上传的 small compilable example 在我的 Visual Studio 中崩溃了,但在 IDEone 中没有。
代码在 Visual Studio 2013 Update 5(x64 版本,v120)中编译。我已将项目的 "Struct Member Alignment" 设置设置为 16 字节,但几乎没有改进,并且在我使用的结构中没有打包 pragma
。错误信息是:
First-chance exception at 0x00007ff7a7dc3533 in PG3Render.exe: 0xC0000005: Access violation reading location 0xffffffffffffffff.
gcc 和 clang 都很好,可以为您的示例制作不会崩溃的非矢量化代码。 (当然,我正在为 Linux SysV ABI 进行编译,其中 none 的矢量 reg 是调用者保存的,因此它们不会生成代码来保存 xmm{6..15}堆叠在第一位。)
您的 IDEone link 也没有表现出崩溃,所以 IDK。我有一些在线编译和 运行 站点可以选择 MSVC。如果您的程序使用 system
到 运行 自身的反汇编程序,您甚至可以从中获取 asm。 :P
您发布的 asm 输出 保证 崩溃,对于任何可能的值 rax
:
00007FF7A7DC3522 movaps xmmword ptr [rax-58h],xmm10
00007FF7A7DC3527 lea rax,[rax+18h]
...
00007FF7A7DC3533 movaps xmmword ptr [rax-68h],xmm11
考虑到LEA,第二个store地址是[init_rax-50h]
,与前面的store只相差8B。一个或另一个会出错。 这似乎是一个您应该报告的编译器错误。
我不知道为什么你的编译器会使用 lea
而不是 add rax, 18h
。它在用 comiss
破坏标志之前就这样做了
出于某种原因,我的一个函数正在调用带有未对齐参数的 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
) 的当前内容保存到堆栈中来开始准备 playground,因此它们可以被后续代码自由使用。 xmm*
寄存器一个接一个地存储到从 [rax-18h]
到 [rax-68h]
的地址,这些地址从 rax=0xe4d987f788
开始很好地对齐到 16 个字节,但在 xmm11
寄存器之前被存储后,rax
增加了 18h,这打破了导致崩溃的对齐方式。 xorps
和 comiss
行是实际代码开始的地方(std::max
与 0 的比较)。当我删除 std::max
时,它运行良好。
您认为这种行为有什么原因吗?
附加信息
我上传的 small compilable example 在我的 Visual Studio 中崩溃了,但在 IDEone 中没有。
代码在 Visual Studio 2013 Update 5(x64 版本,v120)中编译。我已将项目的 "Struct Member Alignment" 设置设置为 16 字节,但几乎没有改进,并且在我使用的结构中没有打包 pragma
。错误信息是:
First-chance exception at 0x00007ff7a7dc3533 in PG3Render.exe: 0xC0000005: Access violation reading location 0xffffffffffffffff.
gcc 和 clang 都很好,可以为您的示例制作不会崩溃的非矢量化代码。 (当然,我正在为 Linux SysV ABI 进行编译,其中 none 的矢量 reg 是调用者保存的,因此它们不会生成代码来保存 xmm{6..15}堆叠在第一位。)
您的 IDEone link 也没有表现出崩溃,所以 IDK。我有一些在线编译和 运行 站点可以选择 MSVC。如果您的程序使用 system
到 运行 自身的反汇编程序,您甚至可以从中获取 asm。 :P
您发布的 asm 输出 保证 崩溃,对于任何可能的值 rax
:
00007FF7A7DC3522 movaps xmmword ptr [rax-58h],xmm10
00007FF7A7DC3527 lea rax,[rax+18h]
...
00007FF7A7DC3533 movaps xmmword ptr [rax-68h],xmm11
考虑到LEA,第二个store地址是[init_rax-50h]
,与前面的store只相差8B。一个或另一个会出错。 这似乎是一个您应该报告的编译器错误。
我不知道为什么你的编译器会使用 lea
而不是 add rax, 18h
。它在用 comiss