我正在阅读“计算机系统:程序员的透视”第三版,3.10.5 Supporting Variable-Size Stack Frames, Figure 3.43中的汇编让我感到困惑。
本书的部分试图解释如何生成可变大小的堆栈帧,并给出了一个C代码及其汇编版本作为示例。
下面是C和程序集的代码(书中的图3.43 ):
我不知道第8-10行在程序集中的用途是什么.为什么不在第7行之后使用movq %rsp, %r8呢?
(a) 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];
}(b)生成的汇编代码的一部分
vframe:
2: pushq %rbp
3: movq %rsp, %rbp
4: subq $16, %rsp
5: leaq 22(, %rdi, 8), %rax
6: andq $-16, %rax
7: subq %rax, %rsp
8: leaq 7(%rsp), %rax
9: shrq $3, %rax
10: leaq 0(, %rax, 8), %r8
11: movq %r8, %rcx
................................
12: L3:
13: movq %rdx, (%rcx, %rax, 8)
14: addq $1, %rax
15: movq %rax, -8(%rbp)
16: L2:
17: movq -8(%rbp), %rax
18: cmpq %rdi, %rax
19: jl L3
20: leave
21: ret以下是我的想法:
在第7行之后,%rsp应该是16的倍数(在调用vframe之前,%rsp应该是16的倍数,因为堆栈帧对齐)。当调用vframe时,%rsp被减去8来保存调用者的返回地址,然后第2行的pushq指令再减去8,在第4行中为16。因此,在第7行的开头,%rsp是16的倍数。在第7行,%rsp被%rax减去。由于第6行将%rax设置为16的倍数,第7行的结果是将%rsp设置为16) 的倍数,这意味着的下4位都是零。在第8行,%rsp+7存储在%rax中,在第9行,%rax逻辑上移动3位,在第10行,%rax*8存储在%r8中。
在第7行之后,%rsp的下4位都是零。在第8行中,%rsp+7只将较低的3位全部删除,而第9行则截断这3位,而在第10行中,%rax*8使结果左移3位。所以最后的结果应该是原始的%rsp (第7行的结果)。
所以我想知道第8-10行是不是没用的。
为什么不直接在第7行之后使用movq %rsp, %r8并删除原来的第8-10行?
发布于 2020-01-03 05:28:24
我认为一个有用的探索性程序是将生成的代码减少到:
.globl _vframe
_vframe:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
leaq 22(, %rdi, 8), %rax
andq $-16, %rax
subq %rax, %rsp
leaq 7(%rsp), %rax
shrq $3, %rax
leaq 0(, %rax, 8), %r8
mov %r8, %rax
sub %rsp, %rax
leave
ret请注意,我刚刚删除了执行任何有用操作的代码,并返回了%r8和%rsp之间的差异。然后一位司机写道:
extern void *vframe(unsigned long n);
#include <stdio.h>
int main(void) {
int i;
for (i = 0; i < (1<<18); i++) {
void *p = vframe(i);
if (p) {
printf("%d %p\n", i, p);
}
}
return 0;
}去查查。他们总是一样的。那么,为什么?这可能是当面对给定的构造(var len数组)时,它是一个标准的代码发射。编译器必须维护某些标准,如可跟踪的调用帧和对齐,os可能只是将此代码作为已知的解决方案发出。可变长度数组通常被认为是语言中的一个错误;这是对c++的一种赞扬,在C中增加了一种半工半读的机制;因此编译器实现者可能不会对代表他们生成的代码给予太大的关注。
https://stackoverflow.com/questions/59558711
复制相似问题