首页 > 解决方案 > gcc如何生成可以使用ENTER指令设置堆栈帧的程序集

问题描述

我用 gcc 生成这样的汇编代码, gcc 可以生成具有 ENTER 用于堆栈帧的代码吗?

.file   "temp.c"
.text
.globl  main
.type   main, @function
main:
pushq   %rbp
movq    %rsp, %rbp
movl    $0, -12(%rbp)
movl    $0, -8(%rbp)
movl    $0, -4(%rbp)
movl    $0, %eax
popq    %rbp
ret
.size   main, .-main
.ident  "GCC: (Ubuntu 7.4.0-1ubuntu1~18.04) 7.4.0"
.section    .note.GNU-stack,"",@progbits

这是原始代码:

 #include <stdio.h>
  int main(){
   int a;
   int b;
   int c;
   a = 0;
   b = 0;
   c = 0;
}

标签: gccassemblyx86

解决方案


GCC 永远不会发射enter,因为与 2 或 3 条单指令的正常帧指针设置相比,它的速度非常慢。

(如果它完全生成一个帧指针;gcc -O1并且更高的启用-fomit-frame-pointer。除了优化大小时,因为x(%rsp)寻址模式使用额外的字节而不是x(%rbp)模式。)

# equivalent to  enter $24, $0  (4 bytes)
    push   %rbp               # 1 byte
    mov    %rsp, %rbp         # 3 bytes
    sub    $24, %rsp          # 4 bytes only for a non-zero immediate

具体来说,在 Skylake 上enter是 12 微指令,每 8 个周期的吞吐量为enter a, 0Agner Fog 的指令表)一个。对于非零嵌套级别,它非常慢,例如87 cycles + 7 * nesting level.

在 Ryzen 上,enter是 12 微指令,每 16 周期吞吐量一个。

leave不过很好:在 Intel CPU 上只有 3 微指令。(这仍然比mov %rbp, %rsp/多一个pop %rbp。3 微指令不包括堆栈同步微指令;即使堆栈引擎之前同步,它也是 3。leave


使用的唯一原因enter是以牺牲速度为代价优化代码大小。但即使gcc -Os对代码大小也没有足够的关心,所以没有选择。

甚至clang -Oz(将使用push $1/pop %rax来节省 2 个字节 vs. mov $1, %eax)不使用enter. (Godbolt 编译器资源管理器

enter 0,0甚至不保存代码大小,所以它只是很糟糕。

我按照手册说程序开始输入 ENTER

这是一个(过时且不推荐)选项。

如果您想编写自己的编译器来生成慢速代码,请继续。


推荐阅读