05月03, 2021

Compiler - 0x00

gcc hello.c -o hello
./hello
Hello, World!

constant folding 常量折叠 (提高执行速度)

把常量表达式在编译时进行运算,程序运行时就可以省略这次运算,获得更快执行速度。

algebraic simplification 代数简化

利用表达式的数学性质,对表达式进行简化。 x*1 x+0 x-0 相当于x, 直接替换成x,。

x*0,用0来代替。

common-subexpression elimination 削除公共子表达式

把多次运算压缩为一次运算,提取出公共子表达式,这样不需要重复计算多次。

dead code elimination 消除无效语句

删除程序中逻辑上执行不到的指令

function inlining 函数内联

把小的函数体直接嵌入到函数调用处,使得函数调用的作用域归零的方法。 注意,如果这个函数是全局的,编译时的优化仅限于同一个文件中定义的函数调用,如果想对程序中所有函数调用都进行函数内敛,那么链接时也需要进行代码优化。

根据优化目的来分类 1,减少执行的指令数:常量折叠。 2,使用更快速的指令:乘法运算换成位移运算,降低运算强度。 3,并行地执行指令:不存在依赖关系的指令同时执行。

寄存器访问速度 比内存访问速度更快,多用寄存器,减少使用访问内存指令。

根据作用范围的优化分类

1, 专注于优化程序的某一部分 local optimization

针对于一部分机器码的指令进行优化窥视孔优化,peep-hole optimization. 将惩罚运算转换成位移运算也算是一种peep-hole optimization。

2,对全局程序进行优化 global optimization 至少以函数为单位的优化。 函数内联

局部优化更为简单,有时候得到的优化效果却更好,是非常实惠的优化方法。

基于作用阶段的优化

编译器可以从以下几个时间节点进行优化。

1,语义分析后,针对抽象语法树的优化

2,生成中间代码后,针对中间代码的优化

3,生成汇编代码后,针对汇编代码的优化

4,连接后,针对程序整体的优化。

越早进行优化,越能针对语言结构,语义进行优化。

抽象语法树阶段,可以简单识别循环,这阶段对循环进行优化。

在中间代码阶段,可以进行语言无关的优化,该阶段可以局部优化,也可以全局优化。

一旦编译成汇编代码,就很难进行大范围优化,只能集中在peep-hole 优化了。

链接后优化:构成程序主体的各种处理流程(function)已经固定,可以对程序整体进行大范围的解析优化。 Intel c compiler, Microsoft Visual Studio 链接时优化。

instruction selection by pattern matching

把中间代码的树,根据预先定义的节点的模式进行分割,从而生成指令,基于模式匹配选择指令。

用好寄存器也是很好的优化,减少内存访问,给局部变量,临时变量分配寄存器 (register allocation)。充分利用为数不多的寄存器,就必须灵活的在不同变量间重复利用寄存器。

为了判断某个寄存器是否同时被两个变量使用,需要分析变量的性质,liveness analysis. 在此基础上使用 graph coloring 的算法来分配寄存器。

CFG分析,以bb为单位。

为对单个函数或者多个函数优化,对代码整体数据流分析,(这个表达式中计算的值在哪了被使用了),使用SSA形式(static single assignment form),这种中间代码形式超越了基本代码块范围的数据流分析。

SSA形式,为了使每个变量只会被赋值(初始化)一次,而为变量起一个别名,并将变量变形。

变量的值非常明确,

代码的Meta info 包括: 代码size,转换前的源代码文件名,符号,重定位信息,调试信息。

symbol, 变量或者函数的名称。



LINK 就是把多个目标文件关联为一个整体。 关联多个目标文件,可以生成使用多个目标文件定义的变量,函数。

relocation 根据程序实际加载到内存时的地址,对目标文件中的代码和数据进行调整。

本文链接:https://harry.ren/post/howtodeveacompiler.html

-- EOF --

Comments