将基于堆栈的语言编译为CIL

时间:2012-07-20 22:21:00

标签: .net compiler-construction stack cil

我有一个基于自定义堆栈的语言,我正在尝试编译到CIL,所以它可以被JITed。语言本身相当简单,因为它只有整数和布尔值。但是,每种数据类型都有一个专用堆栈。语言本身是一个命令流,其中每个命令都可以从任一堆栈中查看,推送和/或弹出值。由命令推送/加扰的整数或布尔值的数量永远不会改变(因此命令具有固定的arity)。还有一个扁平整数数组,语言读取和写入值,表示外部存储器。堆栈本身可以任意深度。

对于诸如“add”,“subtract”等简单命令,将整数堆栈命令转换为CIL几乎非常简单:CIL堆栈可以批量替换整数堆栈(尽管我有一个侧面问题:是否存在限制CIL堆栈的深度,无论是在spec还是在实践中?)但是还有像StoreIfTrue这样的命令,它只会将某个值(从整数堆栈)存储到某个索引处的扁平整数数组(索引也来自整数堆栈)如果布尔堆栈的顶部值为真。因此,对于某些命令,我​​需要同时访问布尔堆栈和整数堆栈。

现在我必须维护一个System.Collections.Generic.Stack来表示布尔堆栈。但我想知道是否有一种已知的算法或方法可以将我的自定义语言的两个堆栈模型“扁平化”为一个与CIL更直接兼容的单个堆栈模型。

2 个答案:

答案 0 :(得分:1)

我认为在一个堆栈中存储两个独立的堆栈是不可能的(至少没有外部临时存储,但是你会得到糟糕的性能)。那是因为无论你使用什么样的表示,都无法以某种方式使两个堆栈的顶部总是接近实际堆栈的顶部。

但CIL不仅拥有堆栈和堆,它还具有局部变量。但是您只能通过常量索引访问局部变量。因此,如果您在编译时始终知道堆栈顶部的索引,并且您也知道堆栈的最大大小,则可以使用局部变量来表示它。但我不认为这两个条件会适用于你的情况。

因此,我认为在您的一个或两个堆栈中使用Stack<T>是最佳选择。

答案 1 :(得分:0)

我无法从您的问题中推断出您是否知道如何从C#生成CIL代码。为此,您可以使用ReflectionCecil

对于虚拟执行系统(VES,将执行CIL指令的虚拟系统的模型),堆栈(和寄存器)中的值没有关联的复杂类型。 VES仅跟踪简单类型(int32,int64,托管对象引用,托管指针和浮点数)。因此,VES无法看到堆栈上的布尔值和整数之间的区别(在内部,VES将布尔值视为32位整数),因此无法使用执行堆栈来模拟布尔值和整数堆栈。您也可以这样做:将布尔值视为整数,将非零整数视为布尔值true。因此,对两个整数进行比较会产生另一个整数。但是,那么你只有一个而不是两个。


修改

啊,我明白了。您的语言旨在成为通用编程语言,因此必须具有高度可靠性,并且对所有可能的输入(包括无效输入)具有一些预定义(或无)行为。通过为每种可能的类型分别使用堆栈,更有可能使用兼容的操作数而不是任何随机的操作数。

由于无法使用单个堆栈来模拟多个堆栈,因此我会为每种类型使用真正的Stack<T>对象,而不是尝试使用CIL堆栈。这有几个好处:

  • 将来更容易添加新类型
  • 允许堆栈边界检查(例如,使操作成为无操作)
  • 比在一个堆栈上混合类型的任何自定义方案更易于管理
  • 随机访问,如果您需要
  • 无需了解CIL内部,因此可以在Mono和.Net上运行
  • CIL堆栈仅用于临时操作数,堆栈帧和返回地址