我在业余时间学习汇编语言,以成为更好的开发人员。
我理解基于堆栈的机器和基于寄存器的机器在概念层面上的区别,但我想知道基于堆栈的机器是如何实际实现的。如果是虚拟机,例如JVM或.NET,在基于寄存器的体系结构上运行,例如, x86或x64,那么它必须在汇编级别使用寄存器(据我所知)。我显然在这里遗漏了一些东西。因此,我不确定汇编语言的区别。
我在这里阅读过文章,例如Stack-based machine depends on a register-based machine?以及维基百科,但我不相信他们会直接回答我的问题。
答案 0 :(得分:7)
基于堆栈的计算机很少在硬件中实现 - 我只听说过一个这样的实现,并且从未有机会在一个计算机上工作。
实际上,堆栈计算机是由本机解释器在基于真实寄存器的处理器上实现的。从本质上讲,理论Stack机器是由真正的基于寄存器的机器模拟的。
所以回答你的问题:虽然堆栈机器的机器代码没有寄存器,但执行这些指令的本机解释器确实有寄存器并将使用它们。
问:那为什么间接? 答:可移植性 - 可以在任意数量的不同基于寄存器的机器上模拟堆栈机器的指令集。这意味着可以在任何具有解释器的计算机上运行相同的JVM应用程序,因此旧的Java口号“一次编写,随处运行”答案 1 :(得分:6)
作为参考,Burroughs B5000和Inmos Transputer是堆栈计算机。 DEC PDP11具有这种灵活的寻址模式,可以用作堆栈机器。我认为Niklaus Wirth's Lilith可能是一台堆叠机器(超过20年前,我的想法正在下滑: - )
他们在查找操作数的指令中确实没有任何寄存器名称/编号,因为它们在堆栈中。
指令可以将立即(常量)值加载到堆栈上,或加载/存储到内存中。
所以他们不是add.w r0, r1, r5
或add.w eax, [#fe34]
。有add.w
。
汇编程序序列的一个例子(完全不准确,它更复杂)可能是
loadstack 0xfe34 -- got fe34 onto stack
loadstackindirect -- use address on the stack, to load the value at that address
add.w -- assumes we already have the other operand on the stack
-- result back onto the stack
要计算并加载数组中的值,可能会使用堆栈,因为可能没有索引寻址模式。
因此指令很小,并且使用堆栈和堆栈指针隐式完成了大量工作。 IIRC Transputers实际上只有三个值的堆栈,编译器(或开发人员)必须确保维护。
XMOS现在卖出一个现代的“等价物”,雇用一些同样的人。
自从我编写Transputer代码已有20多年了,很抱歉有点模糊。
UCSD Pascal系统使用软件定义的虚拟机,它是一个堆栈计算机。我们的想法是制作可移植到新计算机上的东西,但也易于编写,易于编译和合理的性能。它的虚拟机是用自己的Pascal方言定义的。当它被移植到真实计算机时,寄存器将用于保存堆栈指针,并且可能在堆栈顶部如何处理(通过寄存器)方面具有一些独创性,以便获得合理的性能。
答案 2 :(得分:4)
然后它必须在汇编级别使用寄存器
这不是一个要求,处理器有一个cpu堆栈,其行为很像中间语言中的虚拟堆栈。您可以将指令几乎一对一地翻译成cpu指令。当然,基于堆栈的虚拟机很受欢迎的原因之一是,抖动很容易实现。
这样做的唯一挂断是机器代码效率不高。抖动优化器的工作是找到有效使用cpu寄存器的方法,并使代码更快。
基于寄存器的VM存在相反的问题。这是一个难以解决的问题,因为真正的CPU没有与VM一样多的寄存器。因此,抖动必须找到使用堆栈溢出硬件未提供的寄存器的方法。