GCC初窥

这是gcc maillist中某国际友人 laurent@guerby.net 做的2个小时报告的ppt,报告题目为GCC Toulibre 20091216。最近一直想深入了解gcc,而这个ppt基本包含本博想了解的内容,所以将其翻译并分享到这里。翻译过程中,很多地方可能有错,请大家不吝赐教。原版的ppt见文末。

  • 什么是GCC

GCC–GNU Compiler Collection,即GNU 编译器集合。GCC即可作为本地编译器也能作为交叉编译器,它支持很多高级语言和多个编译和目标平台。GCC的网址 http://gcc.gnu.org.它是FSF基金会版权所有的自由软件.

  • GCC支持的语言

已经在代码树中:c(gcc),c++(g++),Java(gcj),Ada(GNAT),Objective-C(gobjc),Objective-C++(gobjc++),Fortran(gfortran),不在代码树中,但已经支持的Modula-2,Modula-3,Pascal(gpc),PL/1,D(gdc),Mercury,VHDL(ghdl).

  • GCC支持的目标平台

已经在代码树中的,可以在gcc-4.5/gcc/config子文件夹中找到,包括:alpha,arc,arm,avr,bfin,cris,crx,fr30,frv,h8300,i386,ia64,iq2000,lm32,m32c,m32r,m68hc11,m68k,mcore,mep,mips,mmix,mn10300,moxie,pa,pdp11,picochip,rs6000,rx,s390,score,sh,soft-fp,sparc,spu,stormy16,v850,vax,vms,xtensa
不在代码数中但已经支持的,可在GNU_Compiler_Collection的Wikipedia中找到,包括PDP-10和Z8000等14中终端.

  • gcc易编译

至少对于本地编译gcc,仅需以下几步

$ tar xfj gcc-4.4.2.tar.bz2
$ mkdir build && cd build
$ ../gcc-4.4.2/configure –prefix=/opt/gcc
$ make bootstrap && make install
$ make -k check

  • gcc编译很快

具体速度依赖编译所用机器,在2×4个核的X86_64上仅需15分钟.在ARM Cortex A8@800Mhz上需要大约7小时.编译需要约512M内存.注意,对于开发版的GCC,因为需要很多内建的检测,所以编译会较慢.

  • 自举

编译器是用程序语言编写并被编译成机器码以便执行的,所以使用特定编程语言写出的编译器是能够编译它自己的(译者注:写编译器的语言和编译器编译的语言要相同),这就是编译器的自举.GCC的自举过程为首先系统编译器编译生成stage1,而后stage1被用来编译它自己生成stage2.接着stage2被用来编译生成stage3,其中stage2和stage3生成的要完全相同.(译者注:现在一个简单的make命令,将自动完成make bootstrap)

  • 编译前的准备工作.

目前多数发布的是4.3或4.4.在debian或者ubuntu上可以使用命令

$apt-get build-dep gcc

来得到编译环境.在windowss上也能通过Cygwin或Mingw得到

  • 生成一个交叉编译器

生成一个交叉编译器更难,因为GCC,GNU libc和Linux内核都需要链接并依赖彼此的头文件和常量.另外你可能还需要代码树之外的补丁.推荐使用预编译好的工具链,如gcc-avr或者是Crosstool-ng之类的工具.

  • GCC项目历史

1985年Richard Stallman开始了GCC作为GNU C编译器的项目;
1991年GCC 1.x=>GCC 2.x;
1997年EGCS启动;
1999年EGCS成为官方GCC2.95,GCC引入委员会;
2001年GCC 3.x;
2005年GCC4.x,并6个月至1年发布一次.

  • 贡献者

下面数据基于快速修改记录处理(quick ChangeLog parsing)生成.依次为:年份==修改记录===邮件数
1998 == 1685 == 107
1999 == 2629 == 196
2000 == 5588 == 326
2001 == 6356 == 355
2002 == 7259 == 371
2003 == 10440 == 443
2004 == 12857 == 440
2005 == 11657 == 407
2006 == 9221 == 356
2007 == 8611 == 366
2008 == 7789 == 357
2009 == 9028 == 341

  • 基于GCC的有偿工作

有Redhat,Novell等系统集成商,AdaCore,CodeSourcery等开发工具提供商,IBM,AMD,Intel,ST半导体等芯片提供商,Google(15人为C++支持工作,15人为GCC架构工作),还有科研机构如INRIA Saclay等.

  • gcc是个相当大的项目

gcc-4.5-20091210/gcc除去测试集,使用SLOCCOUNT计数,并按语言分组(按比例从大到小):ANSI C:1067922(67.18%);Ada:485301(30.53%);asm:30379(1.91%);更多的统计数据如下:
31006 == 108 == asm
170569 == 62 == texi
189382 == 999 == ads
230459 == 714 == h
300237 == 229 == md
707707 == 871 == adb
1365750 == 772 == c
共识别3033097行

  • GCC中的选项

-O0=>无优化,和其他编译器有不同
-O1=>优化,但最小化编译时间
-O2=>有更多耗时的优化
-O3=>非常耗时的优化,如自动内联和向量化
-Os=>代码大小优化
怎样得到-O1具体的优化选项?

$gcc -Q --help=optimizers -O1

输出结果:

The following options control optimizations:
-falign-jumps [disabled]
-falign-labels [disabled]
-falign-loops [enabled]
...

4.3约140个,4.4约160个,4.5约180。这样我们就能查看gcc中O0,O1,O2,O3,Os中所有相关的优化选项。
目标机相关的指令有哪些?运行命令

$ gcc -Q --target-help
Target specific options:
-m128bit-long-double sizeof(long double) is 16
-m32 Generate 32bit i386 code
-m3dnow Support 3DNow! built-in functions
-m64 Generate 64bit x86-64 code
....

此外还有一些选项,如
-march=…. => 选择一个指令集(注意,得到的可执行代码可能不能运行)
-mtune=… => 在指定的指令集中,针对某个目标机做 优化(atom,core2,opteron等)
-native =>自动检测当前处理器

  • 如何找到有趣的选项

http://www.spec.org上找SPECint和SPECfp的详细结果,测量峰值时使用的选项都在文档中,或者使用自动调优工具,如Acovea, http://www.coyotegulch.com/products/acovea/

  • 内建功能(Builtins)

gcc特用的,但一些其他编译器有提供的功能.这些功能很有趣,因为都是机器无关的.
例如:

int __builtin_ffs(unsigned int x)

返回1+x的最小指数(?),或者当x为0时返回0
还有一些机器相关的内联汇编,如asm(“fsinx %1,%0″:”=f” (result):”f”(angle));

  • GCC如何工作?


$ls -l /usr/bin/gcc-4.1
-rwxr-xr-x 1 root root 205952 2006-12-11 00:12 /usr/bin/gcc

可以看到,gcc程序很小,这仅仅是一个驱动器.它将根据命令行调用很多程序来完成任务.真正的编译器如下:

5.3M /usr/lib/gcc/x86_64-linux-gnu/4.1.2/cc1
5.8M/usr/lib/gcc/x86_64-linux-gnu/4.1.2/cc1plus
7.7M /usr/lib/gcc/x86_64-linux-gnu/4.1.2/gnat1
5.2M /usr/lib/gcc/x86_64-linux-gnu/4.1.2/jc1

因此真正的顺序是:gcc=>xx1=>as或ld.(译者注:请参见本文末尾的gcc -v helloworld.c)

  • Binutils是什么?

GCC指示一个文本文件到文本文件的转换器,Binutils做生成可执行机器码的工作.
运行命令

$gcc -S file.c

将只生成汇编文件file.s

  • GCC的内部架构

前端:每种编程语言有一个前端,包含该编程语言的词法分析,语法分析和语义分析,并将其翻译成统一的中间代码表示,接着该中间表示将会lower成GIMPLE。
中端:操作GIMPLE,依次做机器无关和机器相关的转换和优化。
后端:使用目标机器描述生成目标机器汇编文件。
RTL即寄存器转换语言。在优化时,可能需要对程序遍历数以百遍。

  • 目标机器描述

这是一个模式匹配引擎,它描述每个机器指令的作用。它是一个包含分段C语言代码的文本文件。如gcc/config/i386/i386.md.机器描述的质量将影响最终生成的目标代码的质量。仅需对每个目标机创建一个描述,就能将所有的语言移植到该机器上。

  • 关于Moxie

Moxie的博客 http://moxielogic.org/blog/。已有贡献,简化虚拟指令系统结构设计,为整个GNU工具链做贡献,现在已经可以运行Binutils,GCC,qemu和Linux。gcc/config/moxie/*.md有560行,gcc/config/moxie/*少于2000行。

  • 中间代码形式

对于代码:

int f(int x, int y) {
int tmp;
tmp=x;
if (x>0) tmp=-x;
return tmp+y;
}


$ gcc -S -O3 -fdump-tree-all t.c

得到共100多个文件。
例如t.c.003t.original内容如下:

f (int x, int y)
{
int D.2705;
int tmp;
tmp = x;
if (x > 0) goto ; else goto ;
:
tmp = -x;
:
D.2705 = tmp + y;
return D.2705;
}

t.c.024t.ssa如下

f (int x, int y) {
int tmp;
int D.2705;
: tmp_3 = x_2(D);
if (x_2(D) > 0) goto ;
else goto ;
:
tmp_4 = -x_2(D);
:
# tmp_1 = PHI
D.2705_6 = tmp_1 + y_5(D);
return D.2705_6;
}

  • GCC与GDB

使用选项-g生成调试信息。在汇编文集中插入很多运行无关的代码,如DWARF(和ELF).GDB通过读入这些代码来映射运行中每个点的行号,内存你和寄存器。GCC甚至可以在优化时生产调试信息,但是所生成的信息将很难跟踪。

  • GCC和GDB中的bug

GCC可能产生错误的调试信息,GDB可能错误的读入调试信息,用户界面可能和GDB通信时出错,因此在实际中,需要在这几个项目中获得帮助来搞定bug,希望这些项目之间有更好的沟通。

  • GCC中的bug

GCC每次发布都伴随着数以百计的已知bug(其他的编译器也一样),GCC中紧急bug类型(bugzilla中的关键字):wrong-code,accepts-invalid,编译器内部出错时很容易找出、ice-on-valid、ice-on-invalid、wrong-debug、missed-optimization、rejects-valid、memory-hog、compile-time-hog、assemble-failure、build

  • 如何报告GCC bug

链接:http://gcc.gnu.org/bugs 是个很好的必读资源,包含通常的非gcc bug。 若真是bug,可以运行

$gcc -v -save-temps -O1 myfile.c

生成myfile.i文件,通常情况下,添加这两个附件并连同平台名称和编译器输出就足够了。出错和不出错的版本信息也很有帮助。

  • GCC4.5的新特性

在链接 http://gcc.gnu.org/gcc-4.5/changes.html中能找到,具体如下
第一,增加一个全新的链接时优化器(-flto),使用这一选项时,GCC能够为每个输入文件得到一个bytecode表示,并将它写入每个目标文件的特定ELF段中。当目标文件需要链接在一起时,所有的函数体将从这些ELF段中读出,并将其作为转换单元的一部分表示。这将使得过程间优化能在不同的文件间进行(甚至不同的语言间),为生成代码的性能提升提供可能。为使用链接时优化器,-flto选项需要在编译时和最终链接过程中指定,若程序不需要任何符号输出,将-flto和-fwhopr以及-fwhole-program一起使用可以让过程间优化器使用更激进的假设进而提升性能;第二,自动并行化可以作为Graphite的一部分打开,在打开-ftree-parallelize-loops=的同时,指定-floop-parallelize-all可以打开基于Graphite的优化;第三,现在可以在不修改源码的前提下扩展编译器。新加入的选项-fplugin=file.so告诉编译器载入共享目标文件file.so,并将它作为编译器的一部分运行。内部的文档描述了插件和编译器之间交互的细节以及如何交互。见 http://gcc.gnu.org/wiki/GCC_PluginAPI

  • 如何提问和贡献

http://gcc.gnu.org/ 有详细的用户手册。gcc-help@gcc.gnu.org 是gcc帮助用的邮件列表
IRC用户帮助 => irc.freenode.org #gcc
http://gcc.gnu.org/ml =>邮件列表归档
gcc-patches@gcc.gnu.org 补丁
gcc@gcc.gnu.org
gcc-testresults@gcc.gnu.org
IRC开发频道 => irc.oftc.net #gcc

  • GCC外的其他编译器

自由的:LLVM和Open64,有插件时变得很有趣。
非自由的:ICC(Intel),XLC(IBM)…
评测编译器很难,最好基于你自己的代码,做自己的设置。当心bug和技术支持。如果你想做多平台,那GCC是很难搞定的。

  • GCC编译农场(GCC Compile Farm)

链接 http://gcc.gnu.org/wiki/CompileFarm。 目的:为自由软件开发者提供方便访问且多样的体系结构用来编译和测试(并不局限于GCC).由FSF法国提供支持。很多研究所,个人和公司提供机器,主机和帮助。AMD和Genesi贡献了机器。

  • 结论

GCC是一个庞大的老项目,但是它依旧活力十足,并有很多公司提供支持。学习GCC非常艰巨。即便高质量到Bug报告也是对GCC的贡献。

附:在译者电脑上运行gcc -v helloworld.c的输出。

$ gcc -v helloworld.c
Using built-in specs.
Target: i686-pc-linux-gnu
Configured with: /var/tmp/portage/sys-devel/gcc-4.4.2/work/gcc-4.4.2/configure --prefix=/usr --bindir=/usr/i686-pc-linux-gnu/gcc-bin/4.4.2 --includedir=/usr/lib/gcc/i686-pc-linux-gnu/4.4.2/include --datadir=/usr/share/gcc-data/i686-pc-linux-gnu/4.4.2 --mandir=/usr/share/gcc-data/i686-pc-linux-gnu/4.4.2/man --infodir=/usr/share/gcc-data/i686-pc-linux-gnu/4.4.2/info --with-gxx-include-dir=/usr/lib/gcc/i686-pc-linux-gnu/4.4.2/include/g++-v4 --host=i686-pc-linux-gnu --build=i686-pc-linux-gnu --disable-altivec --disable-fixed-point --without-ppl --without-cloog --enable-nls --without-included-gettext --with-system-zlib --disable-checking --disable-werror --enable-secureplt --disable-multilib --enable-libmudflap --disable-libssp --enable-libgomp --enable-cld --with-python-dir=/share/gcc-data/i686-pc-linux-gnu/4.4.2/python --disable-libgcj --with-arch=i686 --enable-languages=c,c++,fortran --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu --with-bugurl=http://bugs.gentoo.org/ --with-pkgversion='Gentoo 4.4.2 p1.0'
Thread model: posix
gcc version 4.4.2 (Gentoo 4.4.2 p1.0)
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=i686'
/usr/libexec/gcc/i686-pc-linux-gnu/4.4.2/cc1 -quiet -v helloworld.c -D_FORTIFY_SOURCE=2 -quiet -dumpbase helloworld.c -mtune=generic -march=i686 -auxbase helloworld -version -o /tmp/ccRoxwan.s
ignoring nonexistent directory "/usr/local/include"
ignoring nonexistent directory "/usr/lib/gcc/i686-pc-linux-gnu/4.4.2/../../../../i686-pc-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/i686-pc-linux-gnu/4.4.2/include
/usr/lib/gcc/i686-pc-linux-gnu/4.4.2/include-fixed
/usr/include
End of search list.
GNU C (Gentoo 4.4.2 p1.0) version 4.4.2 (i686-pc-linux-gnu)
compiled by GNU C version 4.4.2, GMP version 4.3.1, MPFR version 2.4.1-p1.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: bfddefec40932d7559ac2c097447ec05
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=i686'
/usr/lib/gcc/i686-pc-linux-gnu/4.4.2/../../../../i686-pc-linux-gnu/bin/as -V -Qy -o /tmp/cc0ViYlD.o /tmp/ccRoxwan.s
GNU assembler version 2.18 (i686-pc-linux-gnu) using BFD version (GNU Binutils) 2.18
COMPILER_PATH=/usr/libexec/gcc/i686-pc-linux-gnu/4.4.2/:/usr/libexec/gcc/i686-pc-linux-gnu/4.4.2/:/usr/libexec/gcc/i686-pc-linux-gnu/:/usr/lib/gcc/i686-pc-linux-gnu/4.4.2/:/usr/lib/gcc/i686-pc-linux-gnu/:/usr/libexec/gcc/i686-pc-linux-gnu/4.4.2/:/usr/libexec/gcc/i686-pc-linux-gnu/:/usr/lib/gcc/i686-pc-linux-gnu/4.4.2/:/usr/lib/gcc/i686-pc-linux-gnu/:/usr/lib/gcc/i686-pc-linux-gnu/4.4.2/../../../../i686-pc-linux-gnu/bin/
LIBRARY_PATH=/usr/lib/gcc/i686-pc-linux-gnu/4.4.2/:/usr/lib/gcc/i686-pc-linux-gnu/4.4.2/:/usr/lib/gcc/i686-pc-linux-gnu/4.4.2/../../../../i686-pc-linux-gnu/lib/:/usr/lib/gcc/i686-pc-linux-gnu/4.4.2/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=i686'
/usr/libexec/gcc/i686-pc-linux-gnu/4.4.2/collect2 --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 /usr/lib/gcc/i686-pc-linux-gnu/4.4.2/../../../crt1.o /usr/lib/gcc/i686-pc-linux-gnu/4.4.2/../../../crti.o /usr/lib/gcc/i686-pc-linux-gnu/4.4.2/crtbegin.o -L/usr/lib/gcc/i686-pc-linux-gnu/4.4.2 -L/usr/lib/gcc/i686-pc-linux-gnu/4.4.2 -L/usr/lib/gcc/i686-pc-linux-gnu/4.4.2/../../../../i686-pc-linux-gnu/lib -L/usr/lib/gcc/i686-pc-linux-gnu/4.4.2/../../.. /tmp/cc0ViYlD.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i686-pc-linux-gnu/4.4.2/crtend.o /usr/lib/gcc/i686-pc-linux-gnu/4.4.2/../../../crtn.o

相关文章:

This entry was posted in GCC, 编译技术, 编译理论实践和应用 and tagged , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , . Bookmark the permalink.

One Response to GCC初窥

  1. Pingback: 《编译点滴》半年记 « 编译点滴

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

*

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>