xmm8 寄存器值是否在调用中保留?
Is xmm8 register value preserved across calls?
我的 Windows 使用 Visual Studio 2017 编译的程序执行以下操作:
- 调用一个默认参数值为 35.05 的例程。
- 通过C接口初始化Java虚拟机
- 再次调用默认参数值为 35.05 的例程。
在第一次调用中,默认参数得到正确的 35.05。在第二次调用中,该值是垃圾。
我查看了生成的程序集,在第一次使用默认参数调用时,35.05 从内存位置复制到 xmm8
,然后复制到堆栈(它是第 5 个参数)并进行调用:
0033d 48 8b 01 mov rax, QWORD PTR [rcx]
00340 41 b8 14 00 00
00 mov r8d, 20 // a default argument
00346 f3 44 0f 10 05
00 00 00 00 movss xmm8, DWORD PTR __real@420c3333 // this is 35.05
0034f f3 44 0f 11 44
24 28 movss DWORD PTR [rsp+40], xmm8
00356 48 c7 44 24 20
1e 00 00 00 mov QWORD PTR [rsp+32], 30 // a default argument
0035f 45 8d 48 05 lea r9d, QWORD PTR [r8+5]
00363 b2 0f mov dl, 15 // a default argument
00365 ff 90 08 01 00
00 call QWORD PTR [rax+264]
然后调用初始化JVM。
然后在下一次调用时,再次使用xmm8
将值复制到堆栈:
00ce8 48 8b 01 mov rax, QWORD PTR [rcx]
00ceb 41 b8 14 00 00
00 mov r8d, 20
00cf1 f3 44 0f 11 44
24 28 movss DWORD PTR [rsp+40], xmm8
00cf8 48 c7 44 24 20
1e 00 00 00 mov QWORD PTR [rsp+32], 30
00d01 45 8d 48 05 lea r9d, QWORD PTR [r8+5]
00d05 b2 0f mov dl, 15
00d07 ff 90 08 01 00
00 call QWORD PTR [rax+264]
但是现在,xmm8
已经被覆盖了。
如果我取出初始化 JVM 的调用,那么该值将被保留。
问题是,谁错了? JVM 未保留该值,或者 Microsoft 编译器假设 xmm8
值将被保留是错误的。
从 Microsoft docs 总结的 Windows x64 调用约定。
RCX
、RDX
、R8
和 R9
用作输入整数参数。 XMM0L
、XMM1L
、XMM2L
和 XMM3L
是输入浮点参数。 RAX
、R10
、R11
、XMM4
和 XMM5
是易变的。包括 XMM8
在内的所有其他都是非易失性的。
Note that the "Caller/Callee Saved Registers" sub-page is a bit misleading right now because it doesn't include the SSE registers.
更新: 默认情况下,新指令集的任何附加寄存器都是易失性的。这包括 YMM0-15
和 ZMM0-15
以及 ?MM16-31
(如果存在)的上半部分。
我的 Windows 使用 Visual Studio 2017 编译的程序执行以下操作:
- 调用一个默认参数值为 35.05 的例程。
- 通过C接口初始化Java虚拟机
- 再次调用默认参数值为 35.05 的例程。
在第一次调用中,默认参数得到正确的 35.05。在第二次调用中,该值是垃圾。
我查看了生成的程序集,在第一次使用默认参数调用时,35.05 从内存位置复制到 xmm8
,然后复制到堆栈(它是第 5 个参数)并进行调用:
0033d 48 8b 01 mov rax, QWORD PTR [rcx]
00340 41 b8 14 00 00
00 mov r8d, 20 // a default argument
00346 f3 44 0f 10 05
00 00 00 00 movss xmm8, DWORD PTR __real@420c3333 // this is 35.05
0034f f3 44 0f 11 44
24 28 movss DWORD PTR [rsp+40], xmm8
00356 48 c7 44 24 20
1e 00 00 00 mov QWORD PTR [rsp+32], 30 // a default argument
0035f 45 8d 48 05 lea r9d, QWORD PTR [r8+5]
00363 b2 0f mov dl, 15 // a default argument
00365 ff 90 08 01 00
00 call QWORD PTR [rax+264]
然后调用初始化JVM。
然后在下一次调用时,再次使用xmm8
将值复制到堆栈:
00ce8 48 8b 01 mov rax, QWORD PTR [rcx]
00ceb 41 b8 14 00 00
00 mov r8d, 20
00cf1 f3 44 0f 11 44
24 28 movss DWORD PTR [rsp+40], xmm8
00cf8 48 c7 44 24 20
1e 00 00 00 mov QWORD PTR [rsp+32], 30
00d01 45 8d 48 05 lea r9d, QWORD PTR [r8+5]
00d05 b2 0f mov dl, 15
00d07 ff 90 08 01 00
00 call QWORD PTR [rax+264]
但是现在,xmm8
已经被覆盖了。
如果我取出初始化 JVM 的调用,那么该值将被保留。
问题是,谁错了? JVM 未保留该值,或者 Microsoft 编译器假设 xmm8
值将被保留是错误的。
从 Microsoft docs 总结的 Windows x64 调用约定。
RCX
、RDX
、R8
和 R9
用作输入整数参数。 XMM0L
、XMM1L
、XMM2L
和 XMM3L
是输入浮点参数。 RAX
、R10
、R11
、XMM4
和 XMM5
是易变的。包括 XMM8
在内的所有其他都是非易失性的。
Note that the "Caller/Callee Saved Registers" sub-page is a bit misleading right now because it doesn't include the SSE registers.
更新: 默认情况下,新指令集的任何附加寄存器都是易失性的。这包括 YMM0-15
和 ZMM0-15
以及 ?MM16-31
(如果存在)的上半部分。