首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >可变大小堆栈帧的组装:这些堆栈对齐指令在分配VLA时似乎毫无用处?

可变大小堆栈帧的组装:这些堆栈对齐指令在分配VLA时似乎毫无用处?
EN

Stack Overflow用户
提问于 2020-01-02 05:34:20
回答 1查看 210关注 0票数 2

我正在阅读“计算机系统:程序员的透视”第三版,3.10.5 Supporting Variable-Size Stack Frames, Figure 3.43中的汇编让我感到困惑。

本书的部分试图解释如何生成可变大小的堆栈帧,并给出了一个C代码及其汇编版本作为示例。

下面是C和程序集的代码(书中的图3.43 ):

我不知道第8-10行在程序集中的用途是什么.为什么不在第7行之后使用movq %rsp, %r8呢?

(a) C代码

代码语言:javascript
复制
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)生成的汇编代码的一部分

代码语言:javascript
复制
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行?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-01-03 05:28:24

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

代码语言:javascript
复制
.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之间的差异。然后一位司机写道:

代码语言:javascript
复制
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中增加了一种半工半读的机制;因此编译器实现者可能不会对代表他们生成的代码给予太大的关注。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59558711

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档