当前位置: 首页 > 知识库问答 >
问题:

解释器基于堆栈的VM的替代方案

车诚
2023-03-14

在为另一种语言构建解释器时,通常建议创建一个基于堆栈的虚拟机,该虚拟机可以解释实际解释器生成的字节码。然后,解释器将由两部分组成:翻译器,它将高级语言的指令转换为虚拟机的字节码,以及虚拟机本身。

我的问题是:口译语言有哪些替代方案?例如,跳过虚拟机并使用C中的函数实现所有指令是否可能(而且切实可行)?在某种程度上,在我看来这应该是可能的,但也许您最终会实现某种类型的最小VM,以实现更复杂的功能。还有其他选择吗?

共有2个答案

林英武
2023-03-14

你的建议是可能的。C并没有真正让你操作堆栈,当你调用一个函数时,它不知道它周围的局部变量,所以你需要在堆上分配一个内存块来保留一些用于脚本语言局部变量的虚假“堆栈空间”,并将其传递给每个函数(或将其填充到线程全局中)。您还需要一个堆栈的基指针来进行脚本语言的函数调用。

一旦这样做了,您就已经完成了获得基于堆栈的语言所需的大部分工作。所以你最好做剩下的事。要使用实际的堆栈和基指针,必须下拉到机器语言级别。

如果您的语言是基于寄存器的,它仍然需要一个堆栈来访问局部变量(它只是使用它的频率较低),您只是不太将它用于指令参数。如果我可以刑事地简化一下,基于3地址寄存器的VM是基于堆栈的VM的一种特殊情况。

字节码解释器的另一种方法是让指令包含指令ID,然后将其用作函数指针数组的索引,每个函数指针实现一条指令。

显然,这样做会影响性能。如果您的指令所做的足够简单,您可以通过直接在机器代码中实现它们并放弃函数调用的(通常可以忽略不计的)开销来节省CPU周期,甚至可能使用真实堆栈而不是假堆栈。

这完全取决于你的需要。对于大多数情况,尤其是如果这是您的第一个解析器/解释器/VM,我建议使用函数指针数组和假堆栈。它很简单,调试起来不太难,在现代机器上也足够快。您可以稍后再编写一个优化版本,以实现不同的功能。

E、 一种方法是为函数调用生成足够的机器代码,然后在生成的机器代码中插入指向此类函数的指针。因此,每个脚本都成为一个编译代码块,但您不必编写完整的编译器。从那时起,您就可以通过生成汇编程序来改进单个关键指令,将不太常用的东西作为函数。这将代码的局部性提高了一点,这是一个微小的微优化,可以提供帮助。但这只是其中之一。

哦,大约一个月前,我在博客上写了一篇关于如何从初学者的角度制作编译器(和字节码解释器)的文章,这可能会有所帮助:http://orangejuiceliberationfront.com/how-to-write-a-compiler/

燕雨石
2023-03-14

建议创建基于堆栈的VM,因为它们很容易创建。

另一种常见的VM类型是基于寄存器的,其中值存储在寄存器中而不是堆栈中。

还有许多其他类型的解释器和虚拟机。您可以有一个生成解析树的编译器和一个解释这些树的解释器(但如果它是使用递归函数实现的,那么可以认为它仍然是一个基于堆栈的VM)。

使用编译器而不是生成某种机器代码(用于VM或真实机器)来为另一种语言生成代码也并不少见。C是这些类型编译器的常见目标语言,因为C语言及其编译器无处不在。但是你不再有VM或解释器,你只有一个编译器/翻译器。

 类似资料:
  • 问题内容: 普通CPU(例如Android设备)是基于寄存器的计算机。Java虚拟机是基于堆栈的计算机。但是基于堆栈的计算机是否依赖于基于寄存器的计算机工作?由于基于堆栈的计算机不是OS,因此不能单独运行吗?除了JVM,是否有任何基于堆栈的计算机示例?有人说1个操作数,2个操作数;你为什么需要这个? 问题答案: JVM在任何地方都没有提到寄存器的存在。从它的角度来看,内存仅存在于少数几个地方,例如

  • 本文向大家介绍基于Python中random.sample()的替代方案,包括了基于Python中random.sample()的替代方案的使用技巧和注意事项,需要的朋友参考一下 python中random.sample()方法可以随机地从指定列表中提取出N个不同的元素,但在实践中发现,当N的值比较大的时候,该方法执行速度很慢,如: numpy random模块中的choice方法可以有效提升随机

  • 因此,我知道对堆栈中分配的变量调用会导致无效指针错误。 在ED指针中,在实际指针之前分配8个字节,以保留关于指针大小的信息。因此,我想知道是否在一个结构之前做了一个,然后在该结构上调用free,是否可以释放该结构(当然,这偏离了分配这8个字节是所做的唯一额外的事情的假设)。 我想我的最后一个问题是,堆栈变量分配和堆分配之间是否有真正的区别(就后端对内核的调用而言)。

  • 问题内容: 我想知道是否有人可以指出有关解释用提取的Java线程堆栈的信息。 我的情况是我在GlassFish v2.1.1上运行了一个Java EE 5应用程序,该应用程序定期挂起(每天至少挂2次-3次)。要使其再次运行,我必须终止Glassfish进程并重新启动域。 。应用程序通常会变得越来越慢,直到最终完全挂起。一旦挂起,我将无法获得线程堆栈。我已经能够获得一个线程堆栈,因为它变得越来越慢,

  • 虚拟机安装:Ubuntu 12.04(x86) 这个帖子是最简单的漏洞开发教程系列,在互联网上你可以找到很多关于它的文章。尽管它丰富和熟悉,我更喜欢自己写博客文章,因为它将作为我未来许多职位的先决条件! 什么是缓冲区溢出? 将源缓冲区复制到目标缓冲区可能导致溢出 1、源字符串长度大于目标字符串长度。 2、不进行大小检查。 缓冲区溢出有两种类型: 1、基于堆栈的缓冲区溢出 - 这里的目标缓冲区位于堆

  • 当我使用@PostConstruct或InitializingBean.AfterPropertiesSet时,initMethodName为空。因此,这些都不是XML配置(init-method=“some”)的确切替代方案。 我想知道这种微小的不一致背后的原因。以及在Java中设置init-method的方法。