技术文章:编译器的活跃变量分析

活跃变量分析,是分析变量程序里的使用情况,为进一步的优化提供必要的信息。

如果在某行代码里给某个变量赋值,而这个变量后续还会使用,那么这个变量从这行代码(不含这行代码)往后就是活跃的。

int f(int i)

{

int a = 1; // 这个赋值会让a不活跃

a += i; // 变量i从程序的开头到这行都是活跃的,因为这里要用它

return a; // a在最后这两行都是活跃的

}

活跃变量分析,是从后往前分析。

赋值语句(=)会修改变量的活跃情况。

+=之类的读、更新、写运算不会修改活跃情况,因为a += i 相当于a = a + i,运行这行代码要求a之前的值必须有效。

但a = i运行时不需要a之前的值有效,它会直接用i覆盖掉a,这个赋值运算与a之前的值无关,它会让变量a不再活跃。

上面这个简短的程序在进行变量活跃分析时,步骤如下:

1,首先分析最后的语句return a,

它会读取变量a的值作为函数的返回值,所以这行代码要求a之前的值必须是有效的,即a是活跃的。

那么a活跃到哪一行代码呢?

活跃到return a之前的最近的赋值语句,即a = 1。

2,再分析a += i,

它要求a和i都必须是活跃的,因为它会同时使用a和i之前的值

3,最后分析a = 1,

它会把a变得不活跃,但i在这里还是活跃的。

如果程序里有分支或循环语句,活跃变量分析就变得稍微复杂一点。

例如:

int g(int i)

{

int a = 1;

int b = 2;

if (i > 0) a += 3;

else b+= 4;

return a;

}

这段代码会被if else语句划分成多个基本块,每个基本块都有自己的出口活跃变量入口活跃变量

出口活跃变量,是后续的其他基本块要使用的变量,需要在本基本块的出口位置保存

入口活跃变量,是当前基本块要使用的变量,需要在本基本块的入口位置加载

多个基本块的活跃变量分析

上图是对这段代码的活跃变量分析:

1,最后的return a语句只会用到变量a,

它自己是整个函数的出口,所以它只有入口活跃变量(这里是变量a)。

2,有2条路径可以运行到return a语句,所以这两个prev基本块的出口活跃变量都是a。

它们的入口活跃变量各自分别是a和b,因为a += 3只使用a,b += 4只使用b。

3,if (i > 0) 语句的出口活跃变量,是它的所有next基本块的入口活跃变量的并集

它的出口活跃变量是a和b,入口活跃变量是i。

4,a = 1和b = 2语句,它们的出口活跃变量是a和b,

入口活跃变量是i,因为虽然它不使用变量i,但它后续的if语句使用变量i。

如果一个变量在基本块的出口活跃,而基本块内部不会对它进行任何处理的话,它在基本块的入口依然是活跃的。

5,如果某个基本块内部修改了某个变量,而该变量在这个基本块的出口活跃,那么在基本块的出口需要保存它

否则,当后续使用它的时候,它的值就可能失效了。

活跃变量分析的目的,主要是确定变量在哪里会被使用、在哪里会被修改、在哪里需要加载、在哪里需要保存

这些信息,是编译器后端为变量分配寄存器的关键信息。

scf编译器框架的活跃变量分析,主要有2个文件:

scf_basic_block.c,基本块内的活跃变量分析,

scf_optimizer_active_vars.c,活跃变量在多个基本块之间(程序流程图上)的传播。

下一篇文章说说自动内存管理

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章