GCC(v4.1.2)编译器分析

转载自:http://blog168.chinaunix.net/space.php?uid=20528014&do=blog&id=358180

 0   前言

本文的编译选项均在windriver提供的ccppc编译器(GCC v4.1版本)验证通过。

文中多数选项适用于GCC各版本的编译器,某些选项在不同版本中是有区别的,比如优化开关-O系列选项。如果需要在非4.1版本的GNU编译器上使用本书列出的选项,请参考相关文档。

1   名词解释

下面列出了文中用到的一些名词:

definition           定义

reference            声明

 

2   预编译宏定义

GNU编译器预先定义了大量的宏定义,被称为预编译宏。我们能够通过命令“cpp E dM myprog.c”查看定义了那些预编译宏。下面列出了一些重用的宏:

2.1      __cplusplus:

当源码是C++时会被定义。标准定义下,它是包含年月日的字符串,否则它被定义为1

2.2      __DATE__:

一个11个字符的字符串,格式为“May 3 2002

2.3      __TIME__:

一个9个字符的字符串,格式为“181034

2.4      __GNUC__:

用于判断编译器是否为GNU编译器,格式为GNU主版本号,如版本号3.1.2,则这个宏的定义为3

2.5      _GNUC_MINOR__

格式为GNU小版本号,如版本号3.1.2,则这个宏的定义为1

2.6      __GNUC_PATCHLEVEL__:

GNU修订号。如版本号3.1.2,则这个宏的定义为2

2.7      __LINE__:

行号

2.8      __VERSION__

完整的版本号,至少包含主版本号与小版本号

 

3   编译选项

编译选项可用于gcc编译器的编译过程,通常ppc的编译器为ccppc。所有编译选项都必须以连字符开始。绝大多数编译选项都有前缀,下面以不同的前缀分类介绍编译选项。

3.1      前缀“ - -

常用的打开编译选项的方法是一个连字符加上一个字母。现在出现了另一种加选项的方法,两个连字符加上编译选项。许多选项具有以上两种形式,但做的是相同的事。例如:-gdebug其实是一种编译选项的两种写法。

3.1.1  -g

以默认形式产生调试信息。GDB能够利用这些调试信息工作。

在多数情况下,-g使用stabs格式。-g会使用一些额外的调试信息。利用这些额外信息,GDB可以更好的工作,但其它调试器可能会因此而无法工作。如果需要指定调试信息的格式,可以使用带参数的-g命令。如:-gdb-gstabs-gstabs+

3.1.2  -ansi

该命令强制要求编译器按照标准(standard)编译代码。产生的指令不能与标准冲突,但可以使用那些不冲突的GNU扩展。默认情况下,GCC4.1.2编译器对C语言默认ISO C90标准;对C++语言默认支持ISO C++标准。

ansi命令的问题是它的标准是跟编译器有关的。比如GCC2.95版使用的是ISO C89标准。如果需要指定某个特定标准,可以使用-std命令。如果既不使用-ansi,又不使用-std,则默认为-std=gnu89标准。

3.1.3  -std

该命令指定某种特定的语言标准(目前只适用于C/C++语言)。常见的标准如下:

c89ISO C89标准

iso9899:1990ISO C90 ( 相当于是 “-ansi”)

c99ISO C99标准

gnu89:默认配置,ISO C90GNU扩展(包含部分C99特性,如在C中使用注释符“//”)

gnu99ISO C99GNU扩展标准。随着GCCISO C99的特性越来越多的支持,gnu99将会逐步变为默认配置。

3.1.4  -E

只对源码进行预编译。输出以经过预编译的源码的形式显示到标准输出上。不需要预处理的代码会被忽略。

当是由编译选项 E时,可以配合其它一些选项使用,他们是:-dD, -dI, -dM, -dN, -C, -P

3.1.5  llibrary

寻找以library命名的库文件并连接入目标文件。该库文件名字格式为liblibrary.a。例如:-ltest会在连接时寻找库文件libtest.a。使用-l与直接指定一个文件的唯一区别在于-l会在library变为liblibrary.a的名字然后寻找,而且会寻找多个文件夹。-l指定的库会在系统的几个标准文件夹与我们用-L指定的文件夹内搜索。

3.1.6  -Ldir

-l指定的库文件添加搜索路径。

3.1.7  -Idir

添加头文件搜索路径dir

通过-I选项添加的路径会的搜索优先级高于任何其它搜索路径(包括系统默认路径)。以-I指定的多个路径按先后顺序依次搜索。

需要注意的是风河的编译器ccppc好像不会指定标准库,必须要通过-I去指定。

3.1.8  -iquotedir

为以引号引入的头文件添加搜索路径dir。与-I的区别在于只有“#include file”这样的头文件才会在dir中搜索,“#include <file>”添加的头文件不会在dir中搜索。

3.1.9  -Dmacro[=string]

定义一个预定义宏,如果没有“[=string]”,则默认定义宏为1。相当于在源码中加入:

#define macro string

与之相反的选项是-U用于取消宏定义。

3.1.10-Umacro

取消一个宏定义,相当于在源码中加入:

#undef macro

 

3.2      前缀“ -f

f代表flag,也就是标志位。

大多数此类选项有 “开”与“关”两种状态。每个这样的标志位选项以加“no-”前缀做为关闭状态,而以不加“no-”做为打开状态。例如:-fstrict-aliasing代表打开“严格的别名检查”;-fno-strict-aliasing代表关闭“严格的别名检查”。

绝大部分此类选项只有打开关闭两种属性,因此会由其中一种做为默认配置。但也由一小部分选项不是这样。例如,下边的两个选项被用来指定在for循环中声明的变量的范围:

-ffor-scape         -fno-for-scape

这两种都不是默认设置,默认配置与标准有关,这两个选项都是默认配置的变种,因此可以说有三种选择。

所有的-f选项都可以被双连字符前缀代替。如下两个选项是相同的:

-frtti         --rtti

注意,下面列出了部分选项,但并不是所有的,特别是没有包含O的各级别可能会包含的选项。那些选项会在优化那一章中讲解。

3.2.1  dollars-in-identifiers

 

3.2.2   

 

 

3.3      前缀“ -m

m代表machine,这一类命令代表与架构有关的编译选项(Machine-Specific Compiler Options)。

此类选项的大部分都是与特殊的平台(specific platform)有关,都是用来为在某一类平台上所具有的特性产生特殊的代码结构。但也由一选项用于调试目的,或者用来组织目标文件(object files)的段结构。例如,选项“-msdata”用于在目标文件中生成小数据段(small data segment

注:对于这些与特殊平台有关的选项,本文不会涉及太多,只是对一些常用的命令做些说明。

3.3.1  mcpu=8313

 

3.3.2  strict-align

严格的边界检查,不允许做不对齐的访问。该选项是默认配置。

no-strict-align可以关闭该选项

 

3.4      前缀“ -W

W代表Warning

-W选项被用来指定编译器会对那类情况产生Warning。与-f一样,此类选项也具有加前缀“no-”与不加前缀“no-”两类情况。例如,下面的选项将使编译器对函数入参过多情况产生警告(估计与cpu架构有关,powerpc应该会允许8个入参而不会产生警告):

-Wformat-extra-args

下面的选项将允许传入过多入参而不产生告警。

-Wno-format-extra-args

3.4.1  -Wall

打开所有告警。要注意很多告警其实是默认打开的,而另一类告警根本没有选项去控制,。

3.4.2  -Wcomments

当“/*”紧跟在“/*”之后时,或“//”后有反斜杠“/”时会有告警。

3.4.3  -Werror

把告警当作error来处理,这样当有告警时就会停止编译。

 

4   编译优化

指定编译器对代码的优化级别。编译器优化选项主要目的是优化代码大小与代码执行速度,而这两项通常是冲突的,因此优化级别的不同主要体现在对这两个因素的取舍上。默认的配置是“-O0,也就是不优化。

如果使用优化选项-O0(这通常是默认配置),编译器生成的汇编码会与源码的结构匹配。O0优化的缺点是会占用较多内存,而且代码执行效率比较低。根据“存在的就是合理的”这句名言,如果O0优化没有优点的话也不会被各个版本的GNU编译器所支持了。O0优化由于没有进行代码优化,编译速度要快一些。更重要的是,O0优化不会改变代码结构。这两个因素决定了O0对于软件开发阶段来说是及其理想的,调试器可以很容易的跟踪代码执行过程。而其它优化选项,由于可能会重新排布代码顺序,跟踪起来要困难的多。

需要注意的一点是,下列的这些选项只是优化时所包含的一部优化选项,有些是没有列出的。

4.1      -O0 优化

不优化,这也是默认配置

4.2      -O (-O1) 优化

O1优化中,编译器会试图减少代码大小与执行时间,但需要保证这些修改不会影响调试器的工作。下面列出了O1优化包含的编译选项:

4.2.1  defer-pop

函数延迟出栈。

函数返回时,不会立刻把函数出栈,而是等到必要时再一起出栈,用于增加效率。与其相反的编译选项是no-defer-pop,该选项会再每次函数返回后立刻出栈。

经测试没有发现这个参数有明显作用。

4.2.2  delayed-branch

该选项只在支持延迟分支点的机器上有效,现在使用的ppc603内核不支持该选项。

4.2.3  guess-branch-probability

 

4.2.4  cprop-registers

 

4.2.5  loop-optimize

 

4.2.6  if系列

if-conversion

if-conversion2

 

4.2.7  tree系列

tree-ccp

tree-dce

tree-dominator-opts

tree-dse

tree-ter

tree-lrs

tree-sra

tree-copyrename

tree-fre

tree-ch

 

4.2.8  unit-at-a-time

 

4.2.9  merge-constants

 

4.3      -O2 优化

O2级别的优化会打开除了涉及空间与速度交换的几乎所有优化选项。它除了包含O1的所有选项外,还会打开下列的优化选项:

4.3.1  thread-jumps

 

4.3.2  crossjumping

 

4.3.3  optimize-sibling-calls

 

4.3.4  cse系列

cse-follow-jumps

cse-skip-blocks

 

4.3.5  gcse系列

gcse

gcse-lm

 

4.3.6  expensive-optimizations

 

4.3.7  strength-reduce

 

4.3.8  return系列

rerun-cse-after-loop

rerun-loop-opt

 

4.3.9  caller-saves

 

4.3.10peephole2

 

4.3.11sche系列

schedule-insns

schedule-insns2

sched-interblock

sched-spec

 

4.3.12regmove

 

4.3.13strict-aliasing

 

4.3.14delete-null-pointer-checks

 

4.3.15reorder系列

reorder-blocks

reorder-functions

 

4.3.16内存对齐系列

align-functions

align-jumps

align-loops

align-labels

 

4.3.17tree系列

tree-vrp

tree-pre

 

4.4      -O3 优化

 

4.4.1  inline-functions

 

4.4.2  unswitch-loops

 

4.4.3  gcse-after-reload

 

4.5      -Os 优化

Os优化突出空间的优化,所以它会禁止掉O2优化中某些可能会牺牲空间性能的优化选项。

4.5.1  内存对齐系列

align-functions

align-jumps

align-loops

align-labels

这些选项会强制编译后的代码内存对齐,这会提升访问速度,但会增加目标文件的大小,因此Os优化中会禁止这些选项。

4.5.2  reorder系列

reorder-blocks

reorder-blocks-and-partition

 

4.5.3  prefetch-loop-arrays

 

4.5.4  tree-vect-loop-version

 

 

5   链接选项

链接选项用于链接器ld。对于ppc,我们使用ldppc。这些选项用于确定链接的过程于最终结果。

5.1      -X

等价--discard-locals。删除所有的临时本地符号。(在汇编中可以看到这些变量是以“.L”为前缀的一些变量)。

5.2      -r

等价--relocatable。产生可重定向输出。生成的输出文件能够做为ld的输入文件。这通常被称为partial linking。如果是C++文件,需要使用-Ur

该选项生成的目标文件可以不包含库。通常会利用它生成一个不依赖与库的partial文件,然后在把它与各种库,包括sysTbl.o等文件一起链接为一个可执行文件(ELF文件)。

通俗的讲,使用了-r选项,编译器就不会再要求一个函数或变量必须有实现,只要有声明就可以了。这类输出文件不会是最终的目标文件,要么是需要下到已经有一个可执行文件在运行的目标机中与运行,要么还要与其它库文件或目标文件等链接成可执行文件。

5.3      –e MyEntry

等价“--entry=MyEntry”,用于指定MyEntry为入口点。程序会以MyEntry做为入口点开始执行程序,而不是以默认的start做为入口点。

所谓入口点(Entry Point)是程序开始执行后的第一条指令。有下列四种方式指定入口点。链接器(linker)会顺序尝试一下四种方法,直到有一种成功为止:

(1)     通过-e入口指定命令选项指定入口点;

(2)     寻找符号名为start的函数;

(3)     以“.text”段的第一个字节做为入口点(也就是代码段);

(4)     以地址0做为入口点;

 

5.4      -Tsectionname org

等价与“--section-start .sectionname=org”,用于指定目标文件中指定段sectionname的起始地址为or(该地址为绝对地址,不可被重定向)。如果不制定段起始地址,则根据链接器的默认配置生成。可以多次调用该命令指定同一种段,执行结果是一种段被分为多个段。

org的格式必须是十六进制整数。为了于其它链接器兼容,不要在十六进制数前加前缀“0x”。例如,我想把代码段的起始地址指定为“0x13578bdf”,则命令格式为“-Ttext 13578bdf”。

常用的段有bssdatatext三种段,因此有如下三种命令形式(注意:第二种格式在段前有一个“.”):

 

简单格式

复杂格式

代码段

-Ttext org

--section-start .text=org

数据段

-Tdata org

--section-start .data=org

bss

-Tbss org

--section-start .bss=org

 

5.5      -M

等价于“--print-map”。把链接图(link map)打印到标准输出上去。链接图包含如下信息:

(1)  目标文件放入内存后的分布情况,如代码段、数据段的起始地址等。

(2)  符号是如何分配的。

(3)  所有被链接进去的库文件,以及库中引入的符号。

(4)  符号的初始值。

链接图的具体信息请参考链接脚本的相关内容。

5.6      -Map mapfile

-M作用相似,但链接图会被打印到文件mapfile中。

5.7      -defsym

 

5.8      --cref

输出交叉声明表。也就是符号与库文件的对应关系。

Cross Reference Table

Symbol                    File

Test                      test.o

test                      test.o

test1                     test.o

 

如果已经通过“-Map”指定了链接表输出路径,则把交叉声明表输出到连接表文件中;否则输出到标准输出。

从上面的表中可以看到,交叉声明表的格式非常简单,如果需要的话,可以很容易地被脚本(script)处理。符号按字母顺序排序。每一个符号跟一个文件列表,第一个文件是定义它的文件,后面的是声明它的文件。

5.9      --start-group archives --end-group

按组包含库文件。archives应该是一组库文件(Archeive files),他们应该通过文件名显示加载或通过“-l”选项加载。

按组包含的库文件会在链接时被反复的查找未被未被定义的声明。通常,如果通过命令行为链接器传入库文件,他们谁被顺序包含,所以如果前一个库文件用到了后一个库文件的符号,会找不到符号。如果有几个库之间有相互声明的关系,则用常用的命令行方式无法解决。对于这种情况,可以使用这里讲到的按组包含方式。在--start-group--end-group之间的库文件会被做为一个组来使用,链接器会反复搜索这一组库文件,直到找到所有的符号(前提当然是能够找到,呵呵)。

由于组内的库文件会被反复搜索,链接速度会大手影响,因此链接器手册上建议用户只有在库文件之间存在相互声明,且这种声明不可避免时才使用分组方式。

在我们的实际工程中也由使用分组方式的情况,那就是最后链接所有库文件生成可执行文件时。由于库文件很可能存在相互声明,因此分组方式也不可避免。

 

6    参考文献

GCC Complete Reference                     Arthur Griffith

Using the GNU Compiler Collection (GCC)    Richard M. Stallman and the GCC Developer Community

The GNU linker                             Steve Chamberlain, Ian Lance Taylor

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值