【发布时间】:2019-01-06 15:04:51
【问题描述】:
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,然后通过andq 中的指令向下舍入到最接近的16 倍数
andq $-16, %rax
当n 为奇数时,结果值为8n + 8,当n 为偶数时,结果值为8n + 16,并从@987654334 中减去该值@给s2。
让我困惑的是8n + 22。为什么一定是22,而不是16, 17, 18, 19, 20, 21, 23?
【问题讨论】:
-
它不一定是 22,因为
and无论如何都会砍掉低 4 位。 -
任何大于15小于23的数字都可以?gcc刚好选择了22这个数字?
-
GCC7 确实做到了这一点godbolt.org/z/Hedvqs。您希望
(n+15)&-16将大小四舍五入为 16 的倍数。请注意,ABI 要求可变长度数组以 16 字节对齐。