第二章 - 中间语言
约 877 个字 41 行代码 预计阅读时间 3 分钟
编译器和静态分析
编译器 (compiler) 的基本框架:
- 扫词法分析 (scanner):利用正则表达式 (regular expression) 进行词法分析 (lexical analysis),最终生成词元 (token) 到下一步。
- 语法分析器 (parser):利用上下文无关文法 (context-free grammar) 进行语法分析 (syntax analysis),最终生成抽象语法树 (abstract syntax tree,AST) 到下一步。
- 类型检查器 (typre checker):利用属性文法 (attribute grammar) 进行语义分析 (semantic analysis),将 AST 作标记后传输给下一步。
- 转换器 (translator):将被标记的 AST 转换为中间语言 (intermediate representation, IR) 进行静态分析。
- 机器码生成器 (code generator):将 AST 转换为机器码 (machine code)。
因此,静态分析需要先通过编译生成中间语言后才能进行。
抽象语法树和中间表示
我们从一个具体的例子出发,考察 do i = i + 1; while (a[i] > v)
这行代码。
其 AST 如下:

用三地址码表示的 IR 如下:
1 2 3 |
|
参照这个例子,我们总结以下有关 AST 和 IR 的特点:
- AST
- 相对高级而贴近文法结构
- 通常和编程语言类型有关
- 适合于快速的类型检查
- 缺少控制流信息
- IR
- 相对低级而贴近机器码
- 通常和编程语言类型无关
- 简洁而统一的
- 包含控制流信息
- 通常作为静态分析的基础
三地址码
形式
三地址码 (3-address code, 3AC) 最重要的形式特性在于其命令的右式最多只有一个操作符。
举例来说,为了完成
c = a + b + 3
,三地址码下需要通过两步:t1 = a + b
,c = t1 + 3
来完成。
这一特性也是这种形式得名的由来,即每个三地址码中最多有三个地址 (address),其中地址可以是以下类型:
- 变量名:
a
,b
- 常量:
3
- 编译器生成的临时变量:
t1
,t2
指令
常见指令的三地址码形式如下:
- 二元运算:
x = y bop z
- 一元运算:
x = uop y
- 赋值:
x = y
- 无条件跳转:
goto L
- 条件跳转:
if x goto L
- 带关系比较运算的条件跳转:
if x rop y goto L
Soot
Soot 是最流行的 Java 静态分析框架,其采用的中间语言是 Jimple,这是一种带类型的三地址码。
我们将介绍一些 Soot 下,常见代码结构所对应的三地址码。
Note
因为 Soot 是面向工程设计的,因此下文中给出的一些三地址码可能并不典型,即存在很多被「过度优化」而不符合一般逻辑的写法。我们希望读者通过阅读以下例子更多把握三地址码的整体结构形式,而非具体的实现细节。
for 循环
代码:
package nju.sa.examples;
public class ForLoop3AC {
public static void main(String[] args) {
int x = 0;
for (int i = 0; i < 10; i++) {
x = x + 1;
}
}
}
三地址码:
public static void main(java.lang.String[])
{
java.lang.String[] r0;
int i1;
r0 := @parameter0: java.lang.String[];
i1 = 0;
label1:
if i1 >= 10 goto label2;
i1 = i1 + 1;
goto label1;
label2:
return;
}
do-while 循环
代码:
package nju.sa.examples;
public class DoWhileLoop3AC {
public static void main(String[] args) {
int[] arr = new int[10];
int i = 0;
do {
i = i + 1;
} while (arr[i] < 10);
}
}
三地址码:
public static void main(java.lang.String[])
{
java.lang.String[] r0;
int[] r1;
int $i0, i1;
r0 := @parameter0: java.lang.String[];
r1 = newarray (int)[10];
i1 = 0;
label1:
i1 = i1 + 1;
$i0 = r1[i1];
if $i0 < 10 goto label1;
return;
}
方法调用
代码:
package nju.sa.examples;
public class MethodCall3AC {
String foo(String para1, String para2) {
return para1 + " " + para2;
}
public static void main(String[] args) {
MethodCall3AC mc = new MethodCall3AC() ;
String result = mc.foo("hello", "world");
}
}
三地址码:
java.lang.String foo(java.lang.String para1, java.lang.String para2)
{
nju.sa.examples.MethodCall3AC r0;
java.lang.String r1, r2, $r7;
java.lang.StringBuilder $r3, $r4, $r5, $r6;
r0 := @this: nju.sa.examples.MethodCall3AC;
r1 := @parameter0: java.lang.String;
r2 := @parameter1: java.lang.String;
$r3 = new java.lang.StringBuilder;
specialinvoke $r3<java.lang.StringBuilder: void <init>()>();
$r4 = virtualinvoke $r3.<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>(r1);
$r5 = virtualinvoke $r4.<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>(" ");
$r6 = virtualinvoke $r5.<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>(r2);
$r7 = virtualinvoke $r6.<java.lang.StringBuilder: java.lang.String toString()>();
return $r7;
}
public static void main(java.lang.String[])
{
java.lang.String[] r0;
nju.sa.examples.MethodCall3AC $r3;
r0 =:= @parameter0: java.lang.String[];
$r3 = new nju.sa.examples.MethodCall3AC;
specialinvoke $r3.<nju.sa.examples.MethodCall3AC: vois <init>()>();
virtualinvoke $r3.<nju.sa.examples.MethodCall3AC: java.lang.String foo(java.lang.String, java.lang.String)>("hello", "world");
return;
}
类
代码:
package nju.sa.examples;
public class Class3AC {
public static final double pi = 3.14;
public static void main(String[] args) {
}
}
三地址码:
public class nju.sa.examples.Class3AC extends java.lang.Object
{
public static final double pi;
public void <init>()
{
nju.sa.examples Class3AC r0;
r0 := @this: nju.sa.examples.Class3AC;
specialinvoke r0.<java.lang.Object: void <init>()>();
return;
}
public static void main(java.lang.String[])
{
java.lang.String[] r0;
r0 := @parameter0: java.lang.String[];
return;
}
public static void <clinit>()
{
<nju.sa.examples.Class3AC: double pi> = 3.14;
return;
}
}