编译堆栈计算机的局部变量

dav*_*k01 4 compiler-construction variables stack-machine vm-implementation

我正在构建一个从C语言到堆栈机器的玩具编译器,我正处于需要弄清楚如何处理函数和阻塞局部变量的地方.从抽象的角度思考它看起来我在频谱的两端有两个选项:1)为每个变量预处理和预分配堆栈空间,2)向VM添加特殊指令以遍历堆栈.

为每个变量预处理和预分配堆栈空间

这样做的好处是可以提前给我变量的所有地址,这样我就不必非常聪明,或者在虚拟机上添加任何额外的指令来移动堆栈.缺点是它可能非常浪费,因为从不执行但是声明一大堆变量的条件代码将占用大量不必要的空间.例如,

a : t1 = value;
if (test) {
  b : t2; c : t3; d : t4; ...;
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,即使test总是为false,我仍然会为条件分支中的所有变量分配空间.

向VM添加特殊指令以遍历堆栈

我可以想到的另一种方法是为每个变量声明生成代码,然后添加一些特殊的VM指令以在运行时找出这些变量的地址.这解决了浪费堆栈空间的问题,但随后增加了一些计算开销,我可以通过一些缓存方法来解决这个问题.

那么什么是正确的方法,还有另一种我没想到的方法更好吗?

cod*_*eim 5

堆栈计算机的想法是它在操作数堆栈上进行计算.这并不意味着必须将所有内容存储在堆栈中.这是一个普遍的误解.通常,您的本地vaiables/block作用域访问映射到寄存器操作.

.NET CLR和Java都有指令来存储和获取"本地"变量以及其他类型的变量.我建议你效仿,因为你不想走堆栈进行简单的变量访问.这是非常低效的.加载/存储变量应该是有效的,比如寄存器.大多数堆栈机器仍然具有随机存取存储.

在CLR中,我们还在每个方法的开头预先分配所有局部变量.您预先分配的变量可能是显式高级变量和编译器生成的临时变量的混合.但没有什么说他们必须在堆叠上.在我研究过的虚拟机上,我们在一个快速访问区域实现它们,如关联数组或类似矢量的结构.我建议您使用ildasm来反汇编.NET方法,并记下如何声明和处理局部变量.

例:

 total = apples + oranges
Run Code Online (Sandbox Code Playgroud)

地图到:

 ldloc 'apples'   # load a local onto stack
 ldloc 'oranges'  # load a local onto stack
 add              # add 2 operands on stack
 stloc 'total'    # store local from stack
Run Code Online (Sandbox Code Playgroud)

在之前的回答中,我给出了基于堆栈的机器的解释,并与注册机器进行了比较.我希望其中有一些有用的信息./sf/answers/1701089841/

使用简单的Dictionary或HashTable实现stfld(store field)和ldfld(load field)的原型是相当简单的.稍后,您可以优化汇编或运行时以编译对整数引用的基于符号命名的引用,尤其是在不需要按名称运行时查找变量的情况下.但是,对于反射API,您需要实现其他元数据以将数字或指针地址交叉引用回其原始名称.

PS:如果我误解了你的问题,或者你想讨论更多问题,请发表评论.