开发自己的编程语言(五)—— CIL中间代码的生成
2014-01-13 15:36
423 查看
在编译原理中,最难的步骤应该是代码的优化。只有通过不停的优化代码才能得到更好的性能,而性能又是商业编译器的重要指标。这一切就注定了码优化是一个没有止境的过程。但是对于不争早夕的初级语言来说,这一步骤就显得可有可无,因为有足够的时间让我去等待一个结果的出现。就像我花了很长很长的时间去等待斐波那契第30个结果的出现,而一个经过优化的编译器能在很短的时间内完成计算。
除了代码的优化,中间语言的生成好像也是一根难啃的骨头。Snail Language的最终目标是成为一款.NET语言,所以将自己编译成CIL的过程也在所难免。在我实现编译器之前,我对中间语言生成的步骤一直处于雾里看花终隔一层。但随着实践的深入,有一朝我突然顿悟:将snail language翻译成CIL,其实就是用CIL实现当前的语义;和将snail language翻译成java、C的过程是一致的;翻译的过程不存在一个标准的教程,好坏都凭作者对语言的理解能力和写代码的功力。于是乎,我开始了自己的试验。
我的目标是翻译Snail Language的一小段特定的代码到CIL。(目前的release版本只支持下面片段)
Snail Language:
if(1 < 2) { print(100) }
我们用TR()函数对表达式进行翻译,TR函数有两个重载TR(node)和TR("string"),一个对节点进行递归操作,如果当前node是一个终结符,就调用字符串求值。
分析过程如下:
如果是 if 表达,肯定有一个跳转语句,语句有两部分组成ifnode和跳转标签IFLabel:TR(ifnode) + TR("IFEnd");ifnode由两部分组成,括号表达式和块表达式:TR(parent_expr) + TR(Block_expr);
parent_expr:TR(Number1) + TR(Opt) + TR(Number2)。由于在汇编中两个操作符必须先压栈,然后调用操作符。所有表达式的过程如下:
parent_expr:TR(Number1) + TR(Number2) + TR(Opt)。现在例子中的数据为TR(1) + TR(2) + TR("<")。
1和2是操作数需要压栈:TR("ldc.i4 1") + TR("ldc.i4 2") + TR("bgt IFLabel")。这一步表示对两个操作数压栈,然后进行比较:如果number1 > number2,则不执行Block_expr,直接跳转到ifnode的结尾,也就是IFLabel。
对print表达式,也要做节点求值顺序的变换:TR(number) + TR(printnode)。我们知道print的操作数为int,所以就用call void[mscorlib]System.Console::WriteLine(int32)替换print语句。
得到的CIL结果:
ldc.i4 1 ldc.i4 2 bgt IFEnd ldc.i4 100 call void [mscorlib]System.Console::WriteLine(int32) IFEnd:
验证结果:
将上面的CIL片段,放入CIL文件中。.assembly extern mscorlib {}
.assembly Test{.ver 1:0:1:0}
.module test.exe
.method static void main() cil managed
{
.maxstack 3
.entrypoint
ldc.i4 1 ldc.i4 2 bgt IFEnd ldc.i4 100 call void [mscorlib]System.Console::WriteLine(int32) IFEnd:
Exit:
ret
}
执行CIL:
我们用ilasm.exe编译Snail Language生成的CIL。ilasm是一个MS提供的可执行文件,用于将CIL文件编译成exe文件。实验结果和我们预期的一致。> ilasm test.il
> test.exe
==> 100
两种实现方案:
将Snail Language编译成CIL,有两种可选的方案。第一:将Snail用上述的方式打包成exe文件。第二,对于生成的CIL文件,我们不直接打包成exe,而是用snail从新解析执行CIL,得到运行结果。第一种方案完全依赖于.NET runtime,而不需要自己去解析代码的逻辑。第二种方案比较灵活,以后可以进一步脱离.NET,实现自己的虚拟机。软件下载:
http://download.csdn.net/detail/u012813593/6844057选择生成中间语言:菜单 -> 编译 -> 中间语言(打勾)
测试代码:
if(1 < 2) { print(1) } else if (2 < 3) { print(3) } else { print(4) }
结果:
ldc.i4 1 ldc.i4 2 bgt IfEnd1 ldc.i4 1 call void [mscorlib]System.Console::WriteLine(int32) br ElseEnd1 IfEnd1: ldc.i4 2 ldc.i4 3 bgt IfEnd2 ldc.i4 3 call void [mscorlib]System.Console::WriteLine(int32) br ElseEnd2 IfEnd2: ldc.i4 4 call void [mscorlib]System.Console::WriteLine(int32) ElseEnd2: ElseEnd1:
目前还只有实现上述简单的代码,如果其他代码有bug,请关注后期修改。
相关文章推荐
- 基于JSP编译器基本语法的使用详解
- 条款06:若不想使用编译器自动生成的函数,就该明确拒绝
- g++编译 参数 .
- Google C++ unit test 在ARM Android 2.3 上的编译与使用
- 从代码示例了解ECMAScript5新特性
- Java的可移植性受到广泛使用
- C++ .H .CPP
- Windows Server 2003远程桌面多用户连接问题
- centos下安装nginx
- C++对象的内存分析(1)
- C++对象的内存分析(2)
- C++对象的内存分析(4)
- int与char之间的转化 - 整数0-9与对应的字符'0'-'9'之间的转化 C++
- 由浅入深认识CIL的基本构成+CIL操作码速记表+CIL操作码大全速查
- 程序设计者需要谨记的九大安全编码实践
- (转)给入门程序员的忠告
- C struct 中字节对齐问题
- shell脚本基础
- opengl day1
- linux学习