Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 可变大小堆栈框架的组装:这些堆栈对齐指令在分配VLA时似乎无用?_C_Assembly_X86 64_Callstack_Stack Frame - Fatal编程技术网

C 可变大小堆栈框架的组装:这些堆栈对齐指令在分配VLA时似乎无用?

C 可变大小堆栈框架的组装:这些堆栈对齐指令在分配VLA时似乎无用?,c,assembly,x86-64,callstack,stack-frame,C,Assembly,X86 64,Callstack,Stack Frame,我正在阅读《计算机系统:程序员透视图》第三版和3.10.5中支持可变大小堆栈框架的汇编,图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

我正在阅读《计算机系统:程序员透视图》第三版和
3.10.5中支持可变大小堆栈框架的汇编,图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];
}
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的倍数(
%rsp
在调用
vframe
之前应该是16的倍数,因为堆栈帧对齐。调用
vframe
时,
%rsp
减去8以保留调用方的返回地址,然后第2行中的
pushq
指令再减去
%rsp
,第4行中减去16.So在第7行的开头,
%rsp
是16的倍数。在第7行,
%rsp
%rax
减去。由于第6行将
%rax
设置为16的倍数,第7行的结果是将
%rsp
设置为16的倍数,这意味着
%rsp
的低位4位都是零。
然后在第8行,
%rsp+7
存储在
%rax
中,在第9行,
%rax
逻辑右移3位,在第10行,
%rax*8
存储在
%r8

在第7行之后,
%rsp
的低位4位都是零。在第8行
%rsp+7
中,只需将低位3位设为全1,第9行截断这3位,在第10行
%rax*8
中,结果左移3位。因此,最终结果应该是原始的
%rsp
(第7行的结果)

所以我想知道8-10行是否没用


为什么不在第7行之后使用
movq%rsp,%r8
,并删除原来的第8-10行?

我认为一个有用的探索性程序是将生成的代码减少到:

.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;
}
extern void*vframe(无符号长n);
#包括
内部主(空){
int i;

对于(i=0;i<(1Hey@NameNull。不太可能让一个人阅读/拥有你提到的那本书。人们不会走极端,比如找到这本书只是为了回答你的问题。因此,最好在这里提供书中可以看到的所有相关信息。希望你已经阅读了链接。@RC0993,你认为遗漏了什么信息g?这个问题对我来说似乎很完整。NameNull,你的分析是正确的。这不是很好的代码。我能想到的唯一一件事是,编译器有一些常用的代码序列,无论是否需要,它都用于对齐,并且没有动力优化可变大小数组,因为它们不常用。@prl我发现,在一个例子中我提到的这本书的实践问题,它在第4行末尾给出了一些特定的%rsp值,然后让我计算以下修改寄存器的值。这些给定的%rsp值并不都是16的倍数。所以我猜这本书没有假设第7行末尾的%rsp是16的倍数。解决方案是第8-10行的目的是将%r8与8的倍数对齐,以便指针数组对齐(%r8是数组的地址),如果%rsp不是16的倍数,这是有意义的。是的,这是无用且奇怪的。我能够使用gcc4.8.5
-Og
重新编写本书的asm输出。(
-O1
优化了
i
在循环中的存储/重新加载,因为
p[i]
实际上不能别名到
i
,即使它们都
很长
)。因此,与其他一些CS:APP示例不同,这不是作者在伪造的GCC输出中编辑并出错的情况。这种疯狂是真实的。您所询问的指令甚至出现在
-O2
,甚至出现在当前的GCC9.2中!!是的,很明显没有优化,因为sizeof(长)您的测试调用方在调用之前总是将RSP 16字节对齐。看起来VLA分配代码正在将数组地址对齐到
alignof(long*)
,以防RSP甚至没有对齐8。这太疯狂了,当前的GCC仍然这样做。可能这是32位模式遗留下来的样板文件(其中,GCC希望在例如Windows上的4字节对齐堆栈上对int64_t进行8字节对齐),或者从数组元素与alignof()的可能性出发>堆栈对齐?但您的测试证明,考虑到16字节RSP对齐的ABI保证,对于
长*
数组来说,这些东西是多余的。我对VLA是codegen回水的看法有点像“aam”在rdtsc之前,它是猜测cpu频率最稳定的来源。是的,它就在那里,但没有人真正关心它的工作情况。