【发布时间】:2021-01-14 23:49:50
【问题描述】:
我试图了解 GNU 如何解释几件事,所以我的第一个示例非常简单:声明一个整数并打印它。如果没有调用优化,汇编代码如下:
.arch armv8-a .file "ex1.c" .text .section .rodata .align 3 .LC0: .string "%i\n" .text .align 2 .global main .type main, %function main: .LFB0: .cfi_startproc stp x29, x30, [sp, -32]! .cfi_def_cfa_offset 32 .cfi_offset 29, -32 .cfi_offset 30, -24 mov x29, sp str w0, [sp, 28] mov w0, 328 str w0, [sp, 28] ldr w1, [sp, 28] adrp x0, .LC0 add x0, x0, :lo12:.LC0 bl printf nop ldp x29, x30, [sp], 32 .cfi_restore 30 .cfi_restore 29 .cfi_def_cfa_offset 0 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0" .section .note.GNU-stack,"",@progbits
如果编译优化(-O3),汇编代码更简洁:
.arch armv8-a .file "ex1.c" .text .section .rodata.str1.8,"aMS",@progbits,1 .align 3 .LC0: .string "%i\n" .section .text.startup,"ax",@progbits .align 2 .p2align 3,,7 .global main .type main, %function main: .LFB23: .cfi_startproc adrp x1, .LC0 mov w2, 328 add x1, x1, :lo12:.LC0 mov w0, 1 b __printf_chk .cfi_endproc .LFE23: .size main, .-main .ident "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0" .section .note.GNU-stack,"",@progbits
除了 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
【问题讨论】:
-
帧指针用于未优化的代码以帮助调试。优化后的版本还使用了跳转到
printf(尾调用消除)。如果您有动态堆栈分配,也可以使用帧指针。 -
好的,这回答了我的大部分问题。我仍然不完全理解为什么优化的 Fortran 版本使用帧指针(可能是因为“经典”Fortran 不使用指针),因为代码绝对没有使用可分配变量。谢谢。
-
你问错问题了。不要询问无关的 C 代码,而是发布您的 Fortran 代码和它生成的汇编代码,并询问为什么它使用启用了优化的帧指针。
-
我编辑了帖子以包含 Fortran 和汇编代码。来自未优化 C 编译的汇编代码仍然令人感兴趣,因为 GNU 编译器使用帧指针作为优化的 Fortran 编译(这是我试图弄清楚的要点之一)。
标签: assembly fortran gfortran gnu-assembler