C代码:
long vframe(long n, long idx, long *q) {
long i;
long *p[n];
p[0] = &i;
for (i = 1; i < n; i++)
p[i] = q;
return *p[idx];
}
生成的汇编代码的一部分:
long vframe(long n, long idx, long *q)
n in %rdi, idx in %rsi, q in %rdx
Only portions of code shown
vframe:
pushq %rbp Save old %rbp
movq %rsp, %rbp Set frame pointer
subq $16, %rsp Allocate space for i (%rsp = s1)
leaq 22(,%rdi,8), %rax
andq $-16, %rax
subq %rax, %rsp Allocate space for array p (%rsp = s2)
leaq 7(%rsp), %rax
shrq $3, %rax
leaq 0(,%rax,8), %r8 Set %r8 to &p[0]
movq %r8, %rcx Set %rcx to &p[0] (%rcx = p)
...
Code for initialization loop
i in %rax and on stack, n in %rdi, p in %rcx, q in %rdx
.L3: loop:
movq %rdx, (%rcx,%rax,8) Set p[i] to q
addq $1, %rax Increment i
movq %rax, -8(%rbp) Store on stack
.L2:
movq -8(%rbp), %rax Retrieve i from stack
cmpq %rdi, %rax Compare i:n
jl .L3 If <, goto loop
...
Code for function exit
leave Restore %rbp and %rsp
ret Return
书中作者说:
[leaq
指令中
leaq 22(,%rdi,8), %rax
[计算值8n + 22
,然后通过16
中的指令将其舍入到andq
的最接近倍数
andq $-16, %rax
[8n + 8
为奇数时]的结果值为n
,<< [8n + 16
为偶数时的结果为n
,然后从s1
中减去该值得到s2
。 让我感到困惑的是8n + 22
。为什么必须是22
,而不是16, 17, 18, 19, 20, 21, 23
?
C代码:long vframe(long n,long idx,long * q){long i;长* p [n]; p [0] =&i;对于(i = 1; i
并且我尝试通过clang
使用命令来编译源代码:
clang -Og -S source.c -o source.s在两个平台ubuntu18.04和Windows 10中,
汇编文件显示:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movq %rsp, %r8
movq %rsp, %r9
leaq 15(,%rdi,8), %r10
andq $-16, %r10
movq %r9, %rax
...
[clang
甚至从16到23都选择15,
我读了The x86-64 psABI version 1.0,
在3.2.2 The Stack Frame
部分:
输入自变量区域的末尾应对齐16(32或64,如果__m256或__m512在堆栈上传递)字节边界。换句话说,价值(%rsp + 8)始终是控制权转移到16的倍数(32或64)功能入口点。堆栈指针%rsp始终指向最新分配的堆栈帧。所以我认为数字是
22
或15
并不重要,
数字仅导致e1
和e2
的大小
[clang
选择15
,然后当n
为偶数时,e1
和e2
将小于gcc
版本,
关键是保护16个字节与堆栈帧的末尾对齐,
或可变大小堆栈帧中的s2 - s1
的值。