Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/fortran/2.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
Assembly 为什么编译器使用帧指针和链接寄存器?_Assembly_Fortran_Gfortran_Gnu Assembler - Fatal编程技术网

Assembly 为什么编译器使用帧指针和链接寄存器?

Assembly 为什么编译器使用帧指针和链接寄存器?,assembly,fortran,gfortran,gnu-assembler,Assembly,Fortran,Gfortran,Gnu Assembler,我试图理解GNU是如何解释一些事情的,所以我的第一个示例非常简单:声明一个整数并打印它。如果未调用优化,则汇编代码将显示: 如果编译已优化(-O3),则汇编代码将更加精简: 除了p2align 3,7之外,大多数内容都相对简单,在阅读了sourceware上的描述之后,我仍然在弄清楚它。然而,我的主要问题是别的。为什么非优化版本使用帧指针和链接寄存器以及CFA?它试图实现什么?人们可能会想,为什么我会在意,选择优化版。原因是Fortran代码的优化版本通过使用帧指针和链接寄存器恢复为类似于未优化

我试图理解GNU是如何解释一些事情的,所以我的第一个示例非常简单:声明一个整数并打印它。如果未调用优化,则汇编代码将显示:

如果编译已优化(-O3),则汇编代码将更加精简:

除了p2align 3,7之外,大多数内容都相对简单,在阅读了sourceware上的描述之后,我仍然在弄清楚它。然而,我的主要问题是别的。为什么非优化版本使用帧指针和链接寄存器以及CFA?它试图实现什么?人们可能会想,为什么我会在意,选择优化版。原因是Fortran代码的优化版本通过使用帧指针和链接寄存器恢复为类似于未优化的C版本。
Fortran代码只是:

  program integer_printing

  integer (kind=4) a
  a=328
  write (*,*) a
  end
优化后的汇编代码读取

        .arch armv8-a
        .file   "exa1F.f90"
        .text
        .section        .rodata.str1.8,"aMS",@progbits,1
        .align  3
.LC0:
        .string "exa1F.f90"
        .text
        .align  2
        .p2align 3,,7
        .type   MAIN__, %function
MAIN__:
.LFB0:
        .cfi_startproc
        sub     sp, sp, #576
        .cfi_def_cfa_offset 576
        adrp    x0, .LC1
        adrp    x1, .LC0
        add     x1, x1, :lo12:.LC0
        mov     w3, 328
        mov     w2, 5
        stp     x29, x30, [sp]
        .cfi_offset 29, -576
        .cfi_offset 30, -568
        mov     x29, sp
        ldr     d0, [x0, #:lo12:.LC1]
        str     x19, [sp, 16]
        .cfi_offset 19, -560
        add     x19, sp, 48
        mov     x0, x19
        str     w3, [sp, 44]
        str     d0, [sp, 48]
        str     x1, [sp, 56]
        str     w2, [sp, 64]
        bl      _gfortran_st_write
        add     x1, sp, 44
        mov     w2, 4
        mov     x0, x19
        bl      _gfortran_transfer_integer_write
        mov     x0, x19
        bl      _gfortran_st_write_done
        ldp     x29, x30, [sp]
        ldr     x19, [sp, 16]
        add     sp, sp, 576
        .cfi_restore 29
        .cfi_restore 30
        .cfi_restore 19
        .cfi_def_cfa_offset 0
        ret
        .cfi_endproc
.LFE0:
        .size   MAIN__, .-MAIN__
        .section        .text.startup,"ax",@progbits
        .align  2
        .p2align 3,,7
        .global main
        .type   main, %function
main:
.LFB1:
        .cfi_startproc
        stp     x29, x30, [sp, -16]!
        .cfi_def_cfa_offset 16
        .cfi_offset 29, -16
        .cfi_offset 30, -8
        mov     x29, sp
        bl      _gfortran_set_args
        adrp    x1, .LANCHOR0
        add     x1, x1, :lo12:.LANCHOR0
        mov     w0, 7
        bl      _gfortran_set_options
        bl      MAIN__
        mov     w0, 0
        ldp     x29, x30, [sp], 16
        .cfi_restore 30
        .cfi_restore 29
        .cfi_def_cfa_offset 0
        ret
        .cfi_endproc
.LFE1:
        .size   main, .-main
        .section        .rodata.cst8,"aM",@progbits,8
        .align  3
.LC1:
        .word   128
        .word   6
        .section        .rodata
        .align  3
        .set    .LANCHOR0,. + 0
        .type   options.1.2778, %object
        .size   options.1.2778, 28
options.1.2778:
        .word   2116
        .word   4095
        .word   0
        .word   1
        .word   1
        .word   0
        .word   31
        .ident  "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0"
        .section        .note.GNU-stack,"",@progbits

无论出于何种原因,
-O3
在使用GCC为ARM64目标(包括GNU Fortran)编译时,都不会打开-
fomit frame pointer
选项。您需要为编译器优化在非叶函数中使用帧指针:

MAIN__:
        adrp    x0, .LC1
        sub     sp, sp, #560
        adrp    x1, .LC0
        add     x1, x1, :lo12:.LC0
        ldr     d0, [x0, #:lo12:.LC1]
        mov     w3, 328
        mov     w2, 5
        add     x0, sp, 32
        str     x30, [sp]
        str     w3, [sp, 28]
        str     d0, [sp, 32]
        str     x1, [sp, 40]
        str     w2, [sp, 48]
        bl      _gfortran_st_write
        add     x1, sp, 28
        mov     w2, 4
        add     x0, sp, 32
        bl      _gfortran_transfer_integer_write
        add     x0, sp, 32
        bl      _gfortran_st_write_done
        ldr     x30, [sp]
        add     sp, sp, 560
        ret
当编译器将对printf(
bl printf
)的尾部调用更改为跳转(
b\u printf\u chk
)时,您的C示例代码将优化为一个叶函数,该叶函数不调用其他函数。这就是为什么在不使用
-fomit frame pointer
的情况下消除帧指针的原因

请注意,链接寄存器永远不会被优化掉,至少在任何可以返回其调用者的函数中都不会,因为它需要保留该寄存器中的值,因为它包含要返回的地址。在优化的C示例中,编译器不需要保存或恢复链接寄存器(X30)。它只是让它保持不变,所以
\uu printf\u chk
直接返回给示例C函数的调用者。在您的其他示例中,链接寄存器中存储的值会被这些函数进行的函数调用(特别是BL指令)破坏,因此需要保存和恢复


最后,帧指针与C指针无关。编译器使用它访问函数的局部变量,还形成堆栈帧的链接列表,调试器可以使用它创建回溯并检查调用函数的局部变量。然而,在大多数架构上,如果函数进行可变大小的堆栈分配(例如for),则只需要访问局部变量。在这些体系结构上,当局部变量的大小固定时,可以使用堆栈指针来访问局部变量,但是,这是以增加调试难度为代价的,因此这被视为一种优化。

无论出于何种原因,
-O3
在使用GCC为ARM64目标(包括GNU Fortran)编译时,都不会启用-
fomit frame pointer
选项。您需要为编译器优化在非叶函数中使用帧指针:

MAIN__:
        adrp    x0, .LC1
        sub     sp, sp, #560
        adrp    x1, .LC0
        add     x1, x1, :lo12:.LC0
        ldr     d0, [x0, #:lo12:.LC1]
        mov     w3, 328
        mov     w2, 5
        add     x0, sp, 32
        str     x30, [sp]
        str     w3, [sp, 28]
        str     d0, [sp, 32]
        str     x1, [sp, 40]
        str     w2, [sp, 48]
        bl      _gfortran_st_write
        add     x1, sp, 28
        mov     w2, 4
        add     x0, sp, 32
        bl      _gfortran_transfer_integer_write
        add     x0, sp, 32
        bl      _gfortran_st_write_done
        ldr     x30, [sp]
        add     sp, sp, 560
        ret
当编译器将对printf(
bl printf
)的尾部调用更改为跳转(
b\u printf\u chk
)时,您的C示例代码将优化为一个叶函数,该叶函数不调用其他函数。这就是为什么在不使用
-fomit frame pointer
的情况下消除帧指针的原因

请注意,链接寄存器永远不会被优化掉,至少在任何可以返回其调用者的函数中都不会,因为它需要保留该寄存器中的值,因为它包含要返回的地址。在优化的C示例中,编译器不需要保存或恢复链接寄存器(X30)。它只是让它保持不变,所以
\uu printf\u chk
直接返回给示例C函数的调用者。在您的其他示例中,链接寄存器中存储的值会被这些函数进行的函数调用(特别是BL指令)破坏,因此需要保存和恢复


最后,帧指针与C指针无关。编译器使用它访问函数的局部变量,还形成堆栈帧的链接列表,调试器可以使用它创建回溯并检查调用函数的局部变量。然而,在大多数架构上,如果函数进行可变大小的堆栈分配(例如for),则只需要访问局部变量。在这些体系结构上,当局部变量的大小固定时,可以使用堆栈指针来访问局部变量,但这是以增加调试难度为代价的,因此这被视为一种优化。

在未优化的代码中使用帧指针来帮助调试。优化版本还使用了跳转到
printf
(尾部调用消除)。如果有动态堆栈分配,也可以使用帧指针。好的,这回答了我的大部分问题。我仍然不完全理解为什么优化的Fortran版本使用帧指针(可能是因为“经典”Fortran不使用指针),因为代码肯定没有使用可分配变量。谢谢。你问错问题了。与其询问无关的C代码,不如发布您的Fortran代码及其生成的汇编代码,并询问为什么它在启用优化的情况下使用帧指针。我编辑了这篇文章以包含Fortran代码和汇编代码。来自未优化C编译的汇编代码仍然令人感兴趣,因为GNU编译器使用帧指针作为优化的Fortran编译(这是我试图弄清楚的一点)。未优化代码中使用帧指针来帮助调试。优化版本还使用了跳转到
printf
(尾部调用消除)。如果有动态堆栈分配,也可以使用帧指针。好的,这回答了我的大部分问题。我仍然不完全理解为什么优化后的Fortran版本使用
MAIN__:
        adrp    x0, .LC1
        sub     sp, sp, #560
        adrp    x1, .LC0
        add     x1, x1, :lo12:.LC0
        ldr     d0, [x0, #:lo12:.LC1]
        mov     w3, 328
        mov     w2, 5
        add     x0, sp, 32
        str     x30, [sp]
        str     w3, [sp, 28]
        str     d0, [sp, 32]
        str     x1, [sp, 40]
        str     w2, [sp, 48]
        bl      _gfortran_st_write
        add     x1, sp, 28
        mov     w2, 4
        add     x0, sp, 32
        bl      _gfortran_transfer_integer_write
        add     x0, sp, 32
        bl      _gfortran_st_write_done
        ldr     x30, [sp]
        add     sp, sp, 560
        ret