确定堆栈帧的操作数堆栈的大小

c.a*_*ate 0 compiler-construction jvm

我目前正在构建一个简单的虚拟机作为辅助项目,最终目标是从头开始实现我自己的编程语言。无论如何,我试图松散地复制在 JVM 中看到的许多技术。

我注意到当在 JVM 中调用一个函数时,一个堆栈帧被推送到包含三个部分的调用堆栈上……局部变量数组、操作数堆栈和帧数据。我的问题是 JVM 如何知道要为操作数堆栈部分分配多少空间。是否有一个简单的规则来确定给定函数调用的操作数堆栈应该有多大?

最后,如果操作数堆栈填满会发生什么?堆栈框架是否应该扩展自身以进行补偿?还是应该抛出错误?

apa*_*gin 5

JVM 如何知道要为操作数堆栈部分分配多少空间

“帧的操作数堆栈的最大深度在编译时确定,并与与帧关联的方法的代码一起提供。” JVMS §2.6.2 , §4.7.3

如果操作数堆栈填满会发生什么?

如上所述,每个单独帧的操作数堆栈的大小是预先知道的。对于特定方法,计算不能使用比类文件中指定的更多的操作数堆栈,否则类验证将失败。

“在执行过程中,操作数堆栈的任何时候都不能增长到比 max_stack 项所暗示的深度更大的深度。” JVMS §4.9.2

  • §2.5.2 讨论的是调用堆栈(又名 Java 虚拟机堆栈),而不是操作数堆栈。操作数堆栈溢出的唯一原因是操作数堆栈的最大深度不正确,这意味着字节码是非法的,将在验证阶段被拒绝。 (4认同)
  • @c.abate 如果是 Java 字节码,您甚至必须跟踪预期位于操作数堆栈上的类型,以便为分支合并点提供堆栈映射框架。这意味着相同类型的项目必须与下一次迭代中的循环开始时相同,否则它们将不可用。但通常,当您对编程语言的元素进行建模并将源代码转换为树时,这些知识是免费的。 (3认同)
  • @c.abate 在为方法生成代码时跟踪堆栈深度:每次发出增加堆栈的指令时,都会增加堆栈深度。每次发出缩小堆栈的指令时,都会减少堆栈深度。每次当前堆栈深度超过最大值时,都将最大值设置为当前深度。 (2认同)
  • @c.abate 确实如此。编写验证器时,您需要确保不会发生这种情况(即基本块的所有传入边缘都来自具有相同传出堆栈深度的块),但是在计算代码生成器中的深度时,您只需知道(除非出现错误)这不会发生,因为您不会生成执行此操作的代码。 (2认同)