我写了一个简单的C程序test.c
:
#include <stdio.h>
#include <stdlib.h>
int add(int a, int b);
int main()
{
int i=5,j=10;
int result;
result = add(i, j);
printf("result is %d\n", result);
}
int add(int a, int b)
{
return (a + b);
}
我编译了它:
gcc -S -Os -o test.s test.c
我得到了汇编文件test.s
:
.file "test3.c"
.section .rodata
.LC0:
.string "result is %d\n"
.text
.globl main
.type main, @function
main:
.LFB5:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
subq $16, %rsp
.LCFI2:
movl $5, -12(%rbp)
movl $10, -8(%rbp)
movl -8(%rbp), %esi
movl -12(%rbp), %edi
call add
movl %eax, -4(%rbp)
movl -4(%rbp), %esi
movl $.LC0, %edi
movl $0, %eax
call printf
leave
ret
.LFE5:
.size main, .-main
.globl add
.type add, @function
add:
.LFB6:
pushq %rbp
.LCFI3:
movq %rsp, %rbp
.LCFI4:
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -8(%rbp), %eax
addl -4(%rbp), %eax
leave
ret
.LFE6:
.size add, .-add
.section .eh_frame,"a",@progbits
.Lframe1:
.long .LECIE1-.LSCIE1
.LSCIE1:
.long 0x0
.byte 0x1
.string "zR"
.uleb128 0x1
.sleb128 -8
.byte 0x10
.uleb128 0x1
.byte 0x3
.byte 0xc
.uleb128 0x7
.uleb128 0x8
.byte 0x90
.uleb128 0x1
.align 8
.LECIE1:
.LSFDE1:
.long .LEFDE1-.LASFDE1
.LASFDE1:
.long .LASFDE1-.Lframe1
.long .LFB5
.long .LFE5-.LFB5
.uleb128 0x0
.byte 0x4
.long .LCFI0-.LFB5
.byte 0xe
.uleb128 0x10
.byte 0x86
.uleb128 0x2
.byte 0x4
.long .LCFI1-.LCFI0
.byte 0xd
.uleb128 0x6
.align 8
.LEFDE1:
.LSFDE3:
.long .LEFDE3-.LASFDE3
.LASFDE3:
.long .LASFDE3-.Lframe1
.long .LFB6
.long .LFE6-.LFB6
.uleb128 0x0
.byte 0x4
.long .LCFI3-.LFB6
.byte 0xe
.uleb128 0x10
.byte 0x86
.uleb128 0x2
.byte 0x4
.long .LCFI4-.LCFI3
.byte 0xd
.uleb128 0x6
.align 8
.LEFDE3:
.ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-48)"
.section .note.GNU-stack,"",@progbits
我理解所有这些说明,但我真的不明白这些标签的含义。 .LC0
,.LFB5
,.LCFI0
,.LCFI1
,.LCFI2
,.LFE5
,...这些标签由gcc自动生成。为什么需要这些标签?似乎有些标签是多余的。
答案 0 :(得分:5)
编译器将为需要引用地址的任何位置生成标签,无论是跳转指令还是分支指令,还是数据位置。
编译器不需要创建直观命名的标签,因为它们仅由它生成的代码引用并且没有最终用户可见性,因此它生成或多或少的顺序命名标签,并使用一种方案来防止意外创建两个不同地点的相同标签。
使用两个(或更多)标签标记相同位置绝对没有缺点,因此没有尝试避免这种情况。这就是为什么有几个位置有两个顺序标签,没有插入操作。
如果您真的想知道LCx
和LFBx
系列标签的含义,请阅读compiler source code。这是一个非平凡的代码库,因此期望花费数小时来寻找相关模块。
我遇到了挑战,所以有了一些编译器编写经验 - 我发现module /trunk/gcc/dwarf2out.c
似乎使用相同的策略生成标签名称。查看第250行,了解符号含义的简洁线索。这个模块的大部分都决定了标签,但它的长度接近23,000行,所以它可以很好地测试你的好奇心。
答案 1 :(得分:1)
尝试使用gcc -fverbose-asm -fdump-tree-all -S -Os -o test.s test.c
获取更多信息,特别是包含GCC内部表示的部分视图的许多“转储”文件test.c.*
。
不要被明显无用的标签打扰。我猜GCC可以为每个基本块生成一个。
回想一下,GCC在内部表示(Gimple,Tree)上的工作量很大。优化过程(有数百个)正在显着修改这些内部表示。大多数优化都在中端,在Gimple等工作......
我在http://gcc-melt.org/上的幻灯片有更详细的解释(你可以在网上找到很多其他的)。
考虑使用MELT(扩展GCC 4.6或更高版本的域特定语言)来探索(甚至修改)内部GCC表示。 MELT非常适合这个目标。
你的gcc-4.1
已有好几年了。 GCC 4.7 刚刚发布(实际上是4.7.0 second release candidate)。自4.1以来,海湾合作委员会取得了很多进展(2006年出现)。如果你关心优化,你真的应该使用更新的版本(至少4.6)。您可以在gcc@gcc.gnu.org
上查询有关 GCC内部的问题(开发或黑客编译器的列表),但大多数GCC贡献者都忘记了4.1的详细信息。使用gcc-help@gcc.gnu.org
获取有关GCC的一般帮助(即如何构建或使用它)。