我有一种自定义的基于堆栈的语言,我正试图将其编译为CIL,因此它可以是JITed。该语言本身相当简单,因为它只有整数和布尔值。然而,每种数据类型都有一个专用的堆栈。语言本身就是一个命令流,每个命令都可以从任意一个堆栈中窥探、推送和/或弹出值。命令推送/弹出的整数或布尔数永远不会改变(因此命令具有固定的arity)。还有一个平面整数数组,语言可以读取和写入值,表示外部内存。堆叠本身可以任意深度。
对于"add"、"减法"等简单命令,将整数堆栈命令转换为CIL几乎非常简单:CIL堆栈可以大规模替换整数堆栈(尽管我有一个附带问题:无论是在规范中还是在实践中,CIL堆栈的深度都有限制吗?)然而,也有StoreIfTrue这样的命令,如果布尔堆栈的顶部值为true,则仅将值(来自整数堆栈)存储到某个索引处的平面整数数组(该索引也是来自整数堆栈的)。因此,对于某些命令,我需要同时访问布尔堆栈和整数堆栈。
现在,我必须维护一个System.Collections.Generic.Stack来表示布尔堆栈。但我想知道是否有一种已知的算法或方法可以将我的自定义语言的双堆栈模型"扁平化"为与CIL更直接兼容的单堆栈模型。
我认为在一个堆栈中存储两个独立的堆栈是不可能的(至少在没有外部临时存储的情况下,但这样会得到糟糕的性能)。这是因为无论使用何种表示,都无法使两个堆栈的顶部始终接近实际堆栈的顶部。
但是CIL不仅仅有堆栈和堆,它还有局部变量。但是您只能通过常量索引访问局部变量。因此,如果你在编译时总是知道堆栈顶部的索引,并且你也知道堆栈的最大大小,你可以使用局部变量来表示它。但我认为这两个条件在你的情况下不成立。
因此,我认为对一个或两个堆栈使用Stack<T>
是您的最佳选择。
我无法从您的问题中推断出您是否知道如何从C#生成CIL代码。为此,可以使用"反射"或"塞西尔"。
对于虚拟执行系统(VES,将执行CIL指令的虚拟系统的模型),堆栈上(和寄存器中)的值没有相关的复杂类型。VES只跟踪简单类型(int32、int64、托管对象引用、托管指针和浮点)。因此,VES无法在堆栈上看到布尔值和整数之间的差异(在内部,VES将布尔值视为32位整数),因此无法使用执行堆栈来模拟布尔值堆栈和整数堆栈。您也可以这样做:将布尔值视为整数,将非零整数视为布尔值true。因此,对两个整数进行比较会得到另一个整数。然而,你只有一个堆栈,而不是两个。
编辑:
啊,我明白了。您的语言旨在成为一种通用编程语言,因此必须具有高度健壮性,并对所有可能的输入(包括无效输入)具有一些预定义(或无)行为。通过为每种可能的类型设置单独的堆栈,更有可能使用兼容的操作数,而不是任何随机的操作数。
由于不可能使用单个堆栈来模拟多个堆栈,因此我会为每种类型使用一个真正的Stack<T>
对象,而不尝试使用CIL堆栈。这有几个优点:
- 将来更容易添加新类型
- 允许堆栈边界检查(例如,将操作设为无操作)
- 在一个堆栈上混合类型比任何自定义方案都更易于管理
- 随机访问,如果您需要
- 无需了解CIL内部,因此可以在Mono和.Net上运行
- CIL堆栈仅用于临时操作数、堆栈帧和返回地址