编译器设计中的符号分析
符号分析有助于将程序表达式表达为符号表达式。在程序执行期间,函数行为源自其计算的代数表示。通常,在正常的程序执行过程中,程序的数值会被计算出来,但是关于它们是如何实现的信息会丢失。因此,符号分析有助于我们理解不同计算之间的关系。它极大地有助于使用诸如恒定传播、强度降低和消除冗余计算等优化技术来优化我们的程序。它可以帮助我们理解和说明我们项目的基于区域的分析。符号分析有助于我们优化、并行化和理解程序。
例子:
C++
#include
using namespace std;
int main()
{
int a, b, c;
cin >> a;
b = a + 1;
c = a - 1;
if (c > a)
c = c + 1;
return 0;
}
C++
#include
using namespace std;
int main()
{
int a[1000];
for (int induced_loop = 1; induced_loop <= 10;
induced_loop++) {
int induced_var = induced_loop * 10;
a[induced_var] = 0;
}
return 0;
}
C++
#include
using namespace std;
int main()
{
int a[1000];
int induced_var = 0;
for (int induced_loop = 1; induced_loop <= 10;
induced_loop++) {
induced_var += 10;
a[induced_var] = 0;
}
return 0;
}
C++
#include
using namespace std;
int sum() { return 10; }
int main()
{
int a = sum();
int b = a + 10;
int c = a + 11;
return 0;
}
C++
#include
using namespace std;
int main()
{
int gfg = 0; // start of region 1
for (int outer = 100; outer <= 200;
outer++) { // start of region 2
gfg++;
int temp_outer = gfg * 10;
int var = 0;
for (int inner = 10; inner <= 20;
inner++) { // start of region 3
int temp_inner = temp_outer + var;
var++;
} // end of region 3
} // end of region 2
} // end of region 1
C++
#include
using namespace std;
int main()
{
int gfg = 0; // start of region 1
int i;
int j;
for (int outer = 1; outer <= 100;
outer++) { // start of region 2
gfg = i;
int temp_outer = gfg * 10;
int var = 0;
for (int inner = 10; inner <= 20;
inner++) { // start of region 3
int temp_inner = 10 * i + j - 1;
var = j;
} // end of region 3
} // end of region 2
} // end of region 1
In the above code using symbolic analysis we can figure out that “if(c>a)” is never true and the line “c=c+1” is never executed, hence allows the optimizer to remove this block of code
1.仿射表达式:
仿射函数是线性函数。在符号分析中,我们尽可能将变量表示为引用变量的仿射表达式。仿射表达式主要用于数组索引,因此有助于理解我们程序的优化和并行化。
仿射表达式也可以用我们程序中的多次迭代来编写,这通常被称为诱导变量。
C++
#include
using namespace std;
int main()
{
int a[1000];
for (int induced_loop = 1; induced_loop <= 10;
induced_loop++) {
int induced_var = induced_loop * 10;
a[induced_var] = 0;
}
return 0;
}
induced_var takes values 10,20,30….100. induced_loop takes values 1,2,3…10. Hence both induced_loop and induced_var are induction variables of this loop.
上面的程序可以使用强度降低方法进行优化,我们尝试用加法代替乘法运算,这是一种成本较低的运算。
优化代码:
C++
#include
using namespace std;
int main()
{
int a[1000];
int induced_var = 0;
for (int induced_loop = 1; induced_loop <= 10;
induced_loop++) {
induced_var += 10;
a[induced_var] = 0;
}
return 0;
}
有时无法将函数调用后变量持有的值表示为线性函数,但我们可以使用符号分析确定该变量的其他属性,例如两个变量之间的比较,如下例所示。
C++
#include
using namespace std;
int sum() { return 10; }
int main()
{
int a = sum();
int b = a + 10;
int c = a + 11;
return 0;
}
Using symbolic analysis we can clearly state that value of variable a > b
2. 数据流问题:
这有助于我们了解需要保存变量值的位置以及计算循环中的迭代次数。这种技术使用符号映射,它就像一个函数,将程序中的所有变量映射到一个值。考虑下面的代码
C++
#include
using namespace std;
int main()
{
int gfg = 0; // start of region 1
for (int outer = 100; outer <= 200;
outer++) { // start of region 2
gfg++;
int temp_outer = gfg * 10;
int var = 0;
for (int inner = 10; inner <= 20;
inner++) { // start of region 3
int temp_inner = temp_outer + var;
var++;
} // end of region 3
} // end of region 2
} // end of region 1
使用数据流分析,我们尝试将我们的程序划分为不同的区域。然后我们使用符号映射将程序的变量映射到一个值,分析程序并进一步将它们简化为仿射表达式。我们还尝试保持块变量排他性。在上面的例子中,我们看到变量temp_outer在区域 3 中被使用,实际上属于区域 2,因此我们在从程序的符号映射中了解它的性质后尝试将其删除。此外,如果可能,我们会尝试在我们的程序中减少任何类型的操作。因此代码可以简化为:
C++
#include
using namespace std;
int main()
{
int gfg = 0; // start of region 1
int i;
int j;
for (int outer = 1; outer <= 100;
outer++) { // start of region 2
gfg = i;
int temp_outer = gfg * 10;
int var = 0;
for (int inner = 10; inner <= 20;
inner++) { // start of region 3
int temp_inner = 10 * i + j - 1;
var = j;
} // end of region 3
} // end of region 2
} // end of region 1
我们尝试使用块传递函数来摆脱数据流问题,到上面提到的输入函数。
3. 基于区域的符号分析:
基于区域的分析有自下而上传递和自上而下传递两部分。当传递函数在入口处传递符号映射并在出口处传递输出符号映射时,自底向上传递有助于分析区域。而在自顶向下传递中,符号映射的值被传递到我们程序的内部循环。