理解 fastcall 栈帧
Understanding fastcall stack frame
我正在将我的算法移植到 ml64 汇编中,一半用于运动,一半用于查看我实际可以获得多少性能。
无论如何,目前我正在尝试了解堆栈框架设置,据我所知,在此示例中:
push rbp ; inherited, base pointer of caller, pushed on stack for storage
mov rbp, rsp ; inherited, base pointer of the callee, moved to rbp for use as base pointer
sub rsp, 32 ; intel guide says each frame must reserve 32 bytes for the storage of the
; 4 arguments usually passed through registers
and spl, -16 ; 16 byte alignment?
mov rsp, rbp ; put your base pointer back in the callee register
pop rbp ; restore callers base pointer
我没有得到的两件事是
从 RSP 中减去 32 有何作用?据我所知,除了它从一个堆栈帧到另一个堆栈帧的职责之外,它只是另一个寄存器,对吧?我怀疑它会进入另一个堆栈框架而不是在当前堆栈框架中使用。
什么是 SPL,为什么屏蔽它会使某些内容对齐 16 字节?
push rbp ;save non-volatile rbp
mov rbp, rsp ;save old stack
sub rsp, 32 ;reserve space for 32 bytes of local variables = 8 integers
;or 4 pointers.
;this is per the MS/Intel guides. You can use this as temp
;storage for the parameters or for local variables.
and spl, -16 ;align stack by 16 bytes (for sse code)
mov rsp, rbp ;restore the old stack
pop rbp ;restore rbp
How does subtracting 32 from RSP do anything at all
RSP
是堆栈指针,而不是 只是 另一个寄存器。对它做任何事情都会影响堆栈。在这种情况下,它在堆栈上保留 8x4 = 32 个字节的 space 用于放置局部变量。
What is SPL and why does masking it make something 16 byte aligned?
and rsp,-16
强制四个 LSB 为零。由于堆栈向下增长,因此将其对齐 16 个字节。
使用 SSE 代码时需要按 16 字节对齐,x64 将其用于浮点数学运算。具有 16 字节对齐允许编译器使用更快对齐的 SSE 加载和存储指令。
SPL
是RSP
的低8位。为什么编译器选择这样做是没有意义的。
两条指令都是 4 个字节,and rsp,-16
严格来说更好,因为它不会调用部分寄存器更新。
Disassembly:
0: 40 80 e4 f0 and spl,-16 ;bad! partial register update.
4: 48 83 e4 f0 and rsp,-16 ;good
8: 83 e4 f0 and esp,-16 ;not possible will zero upper 32 bits of rsp
[RSP is] just another register, right?
不,RSP 非常特别。
它指向 the stack,这是 PUSH
和 POP
指令作用的地方。
所有局部变量和参数(不适合寄存器的)都存储在堆栈中。
Understanding fastcall
X64 中只有一种调用约定。如果您指定 __fastcall
以外的调用约定,大多数编译器会将其重新映射到 X64 上的 __fastcall
,这会使事情变得更加混乱。
我正在将我的算法移植到 ml64 汇编中,一半用于运动,一半用于查看我实际可以获得多少性能。
无论如何,目前我正在尝试了解堆栈框架设置,据我所知,在此示例中:
push rbp ; inherited, base pointer of caller, pushed on stack for storage
mov rbp, rsp ; inherited, base pointer of the callee, moved to rbp for use as base pointer
sub rsp, 32 ; intel guide says each frame must reserve 32 bytes for the storage of the
; 4 arguments usually passed through registers
and spl, -16 ; 16 byte alignment?
mov rsp, rbp ; put your base pointer back in the callee register
pop rbp ; restore callers base pointer
我没有得到的两件事是
从 RSP 中减去 32 有何作用?据我所知,除了它从一个堆栈帧到另一个堆栈帧的职责之外,它只是另一个寄存器,对吧?我怀疑它会进入另一个堆栈框架而不是在当前堆栈框架中使用。
什么是 SPL,为什么屏蔽它会使某些内容对齐 16 字节?
push rbp ;save non-volatile rbp
mov rbp, rsp ;save old stack
sub rsp, 32 ;reserve space for 32 bytes of local variables = 8 integers
;or 4 pointers.
;this is per the MS/Intel guides. You can use this as temp
;storage for the parameters or for local variables.
and spl, -16 ;align stack by 16 bytes (for sse code)
mov rsp, rbp ;restore the old stack
pop rbp ;restore rbp
How does subtracting 32 from RSP do anything at all
RSP
是堆栈指针,而不是 只是 另一个寄存器。对它做任何事情都会影响堆栈。在这种情况下,它在堆栈上保留 8x4 = 32 个字节的 space 用于放置局部变量。
What is SPL and why does masking it make something 16 byte aligned?
and rsp,-16
强制四个 LSB 为零。由于堆栈向下增长,因此将其对齐 16 个字节。
使用 SSE 代码时需要按 16 字节对齐,x64 将其用于浮点数学运算。具有 16 字节对齐允许编译器使用更快对齐的 SSE 加载和存储指令。
SPL
是RSP
的低8位。为什么编译器选择这样做是没有意义的。
两条指令都是 4 个字节,and rsp,-16
严格来说更好,因为它不会调用部分寄存器更新。
Disassembly:
0: 40 80 e4 f0 and spl,-16 ;bad! partial register update.
4: 48 83 e4 f0 and rsp,-16 ;good
8: 83 e4 f0 and esp,-16 ;not possible will zero upper 32 bits of rsp
[RSP is] just another register, right?
不,RSP 非常特别。
它指向 the stack,这是 PUSH
和 POP
指令作用的地方。
所有局部变量和参数(不适合寄存器的)都存储在堆栈中。
Understanding fastcall
X64 中只有一种调用约定。如果您指定 __fastcall
以外的调用约定,大多数编译器会将其重新映射到 X64 上的 __fastcall
,这会使事情变得更加混乱。