活跃变量分析,是分析变量在程序里的使用情况,为进一步的优化提供必要的信息。
如果在某行代码里给某个变量赋值,而这个变量后续还会使用,那么这个变量从这行代码(不含这行代码)往后就是活跃的。
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 条评论) “” |