4

我将在 x86 中实现一个虚拟机,我想知道什么样的设计会产生最好的结果。我应该专注于什么来挤出果汁?我将在 x86 程序集中实现整个虚拟机。

我没有太多说明,我可以选择它们的形式。这些指令以块的形式直接投射到 smalltalk 的语法中。我给出了我正在考虑的指令设计:

^ ...       # return
^null     # return nothing
object    # address to object
... selector: ... # message pass (in this case arity:1 selector: #selector:)
var := ... # set
var # get

我正在考虑的那种虚拟机:

mov eax, [esi]
add esi, 2
mov ecx, eax
and eax, 0xff
and ecx, 0xff00 # *256
shr ecx, 5          # *8
jmp [ecx*4 + operations]
align 8:
    operations:
dd retnull
dd ret
# so on...
    retnull:          # jumps here at retnul
# ... retnull action
    ret:
# ... ret action
#etc.

不要开始问我为什么需要另一个虚拟机实现。解释性程序不是您在需要时才拿起的库存东西。您在其他地方提出的大多数虚拟机都以性能成本为重,注重可移植性。我的目标不是可移植性,我的目标是性能。

之所以需要这个解释器,是因为 smalltalk 块最终不会以同样的方式被解释:

A := B subclass: [
    def a:x [^ x*x]
    clmet b [...]
    def c [...]
    def d [...]
]

[ 2 < x ] whileTrue: [...]

(i isNeat) ifTrue: [...] ifFalse: [...]

List fromBlock: [
    "carrots"
    "apples"
    "oranges" toUpper
]

我需要来自解释例程的真正好处,那就是选择在哪里读取程序的上下文。当然,好的编译器应该在大多数情况下编译明显的情况,例如:'ifTrue:ifFalse' 或 'whileTrue: ',或列表示例。对口译员的需求不仅会消失,因为您总是可能遇到无法确定该块得到您期望的治疗的情况。

4

6 回答 6

4

我看到这里对可移植性有些困惑,所以我觉得有必要澄清一些问题。这些是我的拙见,因此您当然可以自由反对它们。

如果您认真考虑编写 VM,我假设您遇到过http://www.complang.tuwien.ac.at/forth/threading/,所以我不会详述所描述的技术。

已经提到,以 VM 为目标具有一些优点,例如减少代码大小、降低编译器复杂性(通常转化为更快的编译)、可移植性(请注意,VM 的重点是语言的可移植性,因此 VM本身不可移植)。

考虑到您的示例的动态特性,您的 VM将比其他更流行的编译器更类似于JIT 编译器。因此,尽管 S.Lott 在这种情况下没有抓住重点,但他对 Forth 的提及却非常当场。如果我要为一种非常动态的语言设计一个 VM,我会将解释分为两个阶段;

  1. 生产者阶段根据需要咨询 AST 流并将其转换为更有意义的形式(例如,获取一个块,决定它是应该立即执行还是存储在某个地方以供以后执行)可能会引入新类型的令牌。本质上,您恢复了在此处解析时可能丢失的上下文敏感信息。

  2. 消费者阶段从 1 获取生成的流并像任何其他机器一样盲目地执行它。如果你让它像 Forth 一样,你可以只推送一个存储的流并完成它,而不是跳转指令指针。

正如您所说,仅以另一种方式模仿该死的处理器的工作方式并不能实现您需要的任何动态性(或任何其他值得该死的功能,例如安全性)。否则,您将编写一个编译器。

当然,您可以在阶段 1 中添加任意复杂的优化。

于 2009-01-12T14:19:29.603 回答
2

如果您想要真正快速的东西,请尝试使用LLVM。它可以根据高级程序描述为大多数处理器生成本机代码。您可以使用自己的汇编语言或生成 llvm 结构跳过汇编阶段,这取决于您认为最方便的方式。

我不确定它是否最适合您的问题,但如果我对无法用程序的其余部分编译的代码执行一些性能关键的执行,它绝对是我会使用的。

于 2009-01-12T12:20:31.153 回答
1

大多数时候,解释器的重点是可移植性。我能想到的最快方法是直接在内存中生成 x86 代码,就像 JIT 编译器所做的那样,但是,当然,你不再有解释器了。你有一个编译器。

但是,我不确定在汇编程序中编写解释器会给您带来最佳性能(除非您是汇编程序专家并且您的项目范围非常有限)。使用高级语言可以帮助您专注于更好的算法,例如符号查找和寄存器分配策略。

于 2009-01-12T11:22:58.383 回答
1

您可以使用未编码的指令集加速您的调度例程:

mov eax, [esi]
add esi, 4
add eax, pOpcodeTable
jmp eax

对于 cpu 的 > Pentium 4 上的每个调度,它应该有一个开销 < 4 个周期。

此外,出于性能原因,最好在每个原始例程中递增 ESI (IP),因为递增可以与其他指令配对的可能性很高:

mov eax, [esi]
add eax, pOpcodeTable
jmp eax

〜1-2个循环开销。

于 2010-01-20T13:21:12.987 回答
0

我不得不问,为什么要创建一个注重性能的虚拟机?为什么不直接写 x86 代码呢?没有什么比这更快了。

如果您想要一种非常快速的解释语言,请查看Forth。他们的设计非常整洁,很容易复制。

于 2009-01-12T11:35:45.813 回答
0

如果您不喜欢 JIT 并且您的目标不是可移植性。我想你可能会对 Google NativeClient项目感兴趣。他们做静态分析、沙盒和其他。它们允许主机执行 RAW x86 指令。

于 2009-01-12T12:21:09.967 回答