c/c++编译优化——读书笔记

来源:互联网 发布:甘肃广电网络微信号 编辑:程序博客网 时间:2024/06/08 08:24

2007-11-28

    文章介绍了几本书可以做C/C++语言的学习:

1Advanced C: Techniques and ApplicationsGerald E. Sobelman and

David E. Krekelberg, Que Corporation

2American National Standard for Information Systems—Programming

Language C X3.159-1989, American National Standards Institute

(ANSI standard for C)

3Programming in CSteve G. Kochan, Hayden Book Company

4Programming Language C++, ISO/IEC 14882–1998, American National

Standards Institute (ANSI standard for C++)

5The Annotated C++ Reference Manual, Margaret A. Ellis and Bjarne

Stroustrup, published by Addison-Wesley Publishing Company, Reading,

Massachusetts, 1990

6The C Programming Language (second edition), Brian W. Kernighan and

Dennis M. Ritchie, published by Prentice-Hall, Englewood Cliffs, New

Jersey, 1988, describes ANSI C.

7The C++ Programming Language (third edition), Bjarne Stroustrup, published

by Addison-Wesley Publishing Company, Reading, Massachusetts,

1997

8Understanding and Using COFF, Gintaras R. Gircys, published by O’Reilly

and Associates, Inc.

    TI的代码编译可以分为:Complier/Assembler/Linker,生产Executable COFF File。

2007-12-1

代码优化的方法之一就是使用inline(内联函数),它将大大优化程序的运行速度,但是代码的存储空间将大大增加。当然inline使用时也有一些限制:

函数的返回参数为struct or union

函数的入参为struct or union

函数入参为volatile

函数入参有一个参数列表

声明了一个struct, union, or enum类型

包含一个static变量

包含一个volatile变量

为递归函数

包含一个pragma

函数的堆栈太大(定义了太多的局部变量)

使用cl2000 –v28并且指定-o的选项是最简单的优化法则,默认-o的等级是2。下面对-o等级做简单的解释:

-o0

对程序Control-flow-gragh的简化

将变量在寄存器中分配

循环的轮转

消除未使用的代码

简化表达式和声明

调用声明为inline的函数

-o1(包含-o0所有优化选项)

Performs local copy/constant propagation

去除未使用的声明

消除局部公共表达式

-o2(包含-o1所有优化选项)

循环的优化

消除全局公共表达式

消除全局未使用的声明

-o3(包含-o2所有优化选项)

去除所有没有调用的函数

简化从未使用函数的返回值

使用Inline方法调用小规格函数

重新排列函数的申明使得被调用函数的Attributes被提前得到

Propagates arguments into function bodies when all call sites pass the same value in the same argument position

确定文件级别变量的特性

2007-12-2

对于必须的内存访问使用Volatile关键字。优化器会分析数据流程图尽可能的避免内存访问。如果部分C/C++实现的Code必须通过内存访问获得,则需要使用volatile关键字表明这一点。优化器也不会对volatile变量进行任何优化。

对于-o3等级的优化器,它会自动inline扩展。

优化的具体项。

Cost-Based Register Allocation,根据变量或者编译临时值的类型、用法和使用频率,使用寄存器替代它们。

Alias Disambiguation,避免不同的指针变量指向同一地址。

Data Flow Optimizations,优化器以较小代价替换表达式,检查并去除不需要的assignments,避免对已经计算好的变量进行计算。

Expression Simplification,为了达到最好的效果,编译器将表达式简化成相同的形式,这样将减少指令和寄存器的使用。

Inline Expansion of Run-Time-Support Library Functions,编译器将使用inline code的方式调用较小的run-time-support函数,保存联合函数调用方式尽可能的使用其他优化方式。

Induction Variables and Strength Reduction,Induction variable和strength reduction将去除代码中的循环控制变量。

Loop-Invariant Code Motion,优化器识别出循环中的表达式总是固定值,该表达式的计算可以移到循环的外面,并且循环中的每次表达式的计算被预先计算好的值替换。

Loop Rotation,编译器会在循环的底部估计循环条件,并且保存跳出循环的分支。很多情况下,初始的检查条件和分支会被优化掉。

Register Variables,编译器将尽可能的使用寄存器存储局部变量、参数、临时变量,访问寄存器中的变量比访问内存更加方便,而且存储在寄存器中的指针更加高效。

Register Tracking/Targeting,编译器会检查寄存器中的内容避免在同一内容再次从内存中取出,变量、常量、结构等将通过straight-line code跟踪。Register targeting直接将需要的表达式转换成特殊寄存器,像分配寄存器变量或者从函数返回值。

Tail Merging,如果希望优化代码大小,Tail merging将对一些函数非常有效。Tail merging在相同指令的结尾找到相同的代码段,这段相关的指令将被写在相同的block中,这些代码所在的位置有新的分支指向block,这样就只剩下一段新的指令,而不是原来重复的几处。

Removing Comparisons to Zero,因为大部分ALU指令能够调整寄存器的状态,清楚的与0比较将是不必要的。如果先前的指令能够适当的设置寄存器,TMS320C28x C/C++编译器将可以不与0比较。

2007-12-6

链接C/C++程序。无论采用何种方法调用链接器,当连接C/C++程序时必须注意一些特别的需求:

Include 编译器的run-time-support library。

指明初始值的类型。

决定怎么在内存空间中分布代码。

Initialized sections

Name

Description

Restrictions

.cinit

C初始记录对于初始化全局变量和静态变量

Program

.const

全局的初始化静态常量,如字符串

Low 64K data

.econst

Far静态变量

Anywhere in data

.pinit

全局构造表(C++)

Program

.switch

执行switch状态的表

Program/Low 64K data(with –mt only)

.text

可执行代码和常量

Program

Uninitialized sections

Name

Description

Restrictions

.bss

全局变量和静态变量

Low 64K data

.ebss

Far全局变量和静态变量

Anywhere in data

.stack

堆栈空间

Low 64K data

.sysmem

函数通过Malloc申请的内存空间

Low 64K data

.esysmem

函数通过Far_Malloc申请的内存空间

Anywhere in data

2008-1-3

TMS320C28x支持C++,标准为ISO/IEC 14882-1998

2008-1-12

一个函数调用另一个函数时,需要进行以下步骤:

1) 被调用的函数并不需要保存寄存器变量,但是当函数返回的时候,返回值需要保存在Stack中;

2) 如果被调用的函数返回参数为结构体,则调用函数将会为该结构申请一个内存空间,并且将该内存空间的地址作为被调用函数的第一个参数;

3) 被调用函数的参数是通过寄存器存放,必要时,可以存在放在Stack中。参数传递的规则如下:

 a) 32-bit的参数(longs或者floats),第一存放在32-bit ACC(AH/AL)。其他32-bit参数或者函数指针以reverse order存放在Stack中。

 b) 指针参数存放在XAR4和XAR5中,其他所有参数存放在Stack中。

 c) 剩下的16-bit参数按照次序存放在AL、AH、XAR4、XAR5中(这些寄存器并没有使用的情况下)。

4) 所有不存放在寄存器中的参数都以reverse次序push到Stack中(最左边的参数在stack中位置为stack的末尾)。所有32-bit参数在stack中以偶数地址为边界。

结构参数事实上是以结构体的地址作为参数传入,被调用的函数必须有一个本地局部变量copy。

如果函数以省略号进行申明,表示函数可以接受变长的参数,这边进行了简单的修正。最后一个明确的参数存放在stack中,所以它的stack地址可以作为读取其他非申明参数的索引。

5) 调用者使用LCR指令调用函数,RPC寄存器变量将被push到stack中,函数的返回值将被存放在RPC寄存器中。

一个被调用函数需要进行以下步骤:

1) 如果被调用函数修改了XAR1、XAR2、或者XAR3,则必须保存修前的值,因为调用函数在这些寄存器中保存了变量,其他寄存器使用之前并不需要预先保存;

2) 被调用函数在stack中为局部变量、临时变量、函数可能使用的参数分配了足够的内存空间,在函数初始化时增加常量到SP寄存器中,内存空间的分配会发生;

3) 如果被调用函数需要使用结构体参数,它只会接收到一个结构体指针。如果被调用函数需要对该结构进行写操作,该结构体必须在stack中有一个本地保存的局部变量,接收的结构体指针被copy到本地保存的局部变量中。如果对于该结构没有写操作,被调用函数直接使用该指针参数作为引用。

当被调用函数需哟啊接收一个结构体参数时,必须谨慎的声明函数,包括它们被使用的地方(结构体参数为作为地址传递的)和它们被声明的地方(函数知道copy该结构体到本地局部变量)。

4) 被调用函数执行函数的code;

5) 被调用函数返回值被保存在寄存器中,规则如下:

 16-bit 整形变量:AL

 32-bit 整形变量:ACC

 16-或者22-bit 指针:XAR4

如果函数返回结构体,调用者会为这个结构体申请内存空间,同时将被调用函数的返回内存空间地址传递进去(XAR4),对于返回结构体,被调用函数通过外部参数copy结构体到内存块中。

通过这种方法,调用者可以通知被调用函数是否返回结构体。

6) 被调用函数分配内存通过减去之前加在SP的值。

7) 被调用函数恢复之前Step1保存的所有寄存器变量。

8) 被调用函数通过LRETR指令返回。PC使用RPC寄存器保存的变量,之前RPC的值从stack中pop出,并保存。

2008-1-13

写中断程序时,需要注意以下几点:

1) 中断程序没有参数,即使声明了,它们也会被忽略掉;

2) 中断程序可以被一般的C/C++代码调用,但是其效率很低,因为调用时会保存所有的寄存器;

3) 中断程序能够响应一个中断或者多个中断。编译器不会为中断增加特殊代码,除了系统复位中断程序:c_init00。当进入中断程序时,不能认为run-time的stack已经建立起来了,因此,你无法分配局部变量以及保存任何信息在run-time stack中。

4) 为了使得中断程序和中断结合,中断函数的地址必须放在合适的中断向量表中。可以使用assembler和linker通过.sect创建中断地址表。

5) 在汇编程序中,记得在符号名称前增加下划线,例如:c_int00到_c_int00。

c_int00函数执行了以下操作,初始化C/C++环境:

1) 为run-time stack申请内存空间,设置初始的stack pointer变量;

2) 初始化寄存器状态位,并设置寄存器为期望值;

3) 将.cinit内初始化表的数据copy到申请内存空间的.bss内,在RAM自动初始化模式下,loader在函数运行前进行这一步的操作(而不是由boot程序);

4) 执行.pinit段中断全局构造;

5) 调用main()函数开始C/C++程序。

原创粉丝点击