仿真就是在一个具有某种接口和功能的系统或子系统上实现另一种与之具有不同接口和功能的系统或子系统的过程。
指令集仿真可以使用多种方法来实现,这需要不同数量的计算资源,并且提供不同的性能和可移植性。其中的一个极端就是直接的解释技术,另一个极端就是二进制翻译。解释包括取一条源指令,对其进行分析,执行需要的操作,再取下一条源指令这样一个循环过程,所有工作都是由软件完成的。另一方面,二进制解释试图分摊取值和分析的代价,它将源指令块翻译为目标指令块,并且将翻译后的代码保存起来以便反复使用。
其中使用了表驱动法的技术,并对其进行了某些扩展和优化
基本的解释
狭义上,一个解释器程序在一台实现源ISA的及其的完整结构状态上仿真运行,这些状态包括所有的结构寄存器和主存。客户机内存的印象,包括程序代码和程序数据,被保存在有解释器维护的内存区中。解释器的内存中还保存着一张称之为上下文块的表,它包含了源结构状态的不同部件。
一个简单的解释器是通过逐条指令地执行源程序来运行的,它根据指令读取并修改源状态。这样的解释器常被称为译码分派(decode-and-dispatch)解释器,因为它是围绕一个主循环来组织的,即译码一条指令,然后将其分派给基于指令的解释历程。
线索解释
译码-分派解释器的主分派循环包含许多直接和间接的分支指令。这些分支依靠硬件实现,它们往往会降低性能,特别是在它们难以被预测的时候。除了在循环顶部测试暂停或中断之外,还有针对switch语句的寄存器间接分支,到解释器历程的分支,从解释器历程返回的二级寄存器间接分支,一直最后的结束循环分支。其区别如下:
预译码和直接线索解释
尽管间接线索解释器消除了集中分派循环,但是集中分派表还是会带来开销。在这个表中查找一个解释器历程仍然需要内存访问和寄存器间接分支指令。为了获得更高的效率,消除对集中表的访问时值得的。
进一步的观察发现,每次遇到一条指令,都会调用一个解释器历程。这样当同一源指令被解释多次时,对于这条指令的每个动态实例,检查指令和提取各个域的过程必须被重复进行。如果这些重复的操作仅仅被执行一次,将提取出的信息以中间形式保存,然后在每次这条指令被仿真时复用,这样看来就更高效了。这个过程称为预译码。
基本的预译码
预译码涉及解析一条指令并且将它表示成能简化解释的形式。
直接线索解释
尽管间接线索解释具有易于移植的优点,但是由分派表引起的间接性同样有性能代价:要访问这个表就需要一次内存查找。为了去除分派表查找引起的间接层,包含在中间代码中的指令代码可以用解释器历程的实际地址来代替。这样尽管速度快了,但是这使得中间形式变得依赖于解释器历程的精确位置,从而限制了可移植性。如果解释器代码被移植到一个不同的目标机器上,就必须为执行它的目标机器重新生成。然而一些编程技术和编译器特性可以在某种程度上减轻这一问题。
解释一个复杂的指令集
二进制翻译
把每个单独的源二进制指令映射到它自己的定制目标代码上,性能将会显著增强。这种将源二进制程序转化为目标二进制程序的过程称为二进制翻译。对预译码(带有线索解释)和二进制翻译,在两种情况下,起初的源代码都被转化为另一种形式。但是在预译码的情况下,仍然需要解释器例程,而在二进制翻译中转化后的代码被直接执行。
代码发现和动态翻译
代码发现的问题
在使用传统ISA的实际代码中,尤其是CISC ISA,代码发现会因为指令长度可变、寄存器间接跳转、在指令中穿插数据和为对齐指令所做的填充等成为一个问题。
代码定位的问题
源代码中的地址被翻译到目标代码中
增量式预译码和翻译
在前面提到的两种情况下,通常的解决方案是在程序正运行在实际输入数据时,即动态地翻译二进制代码,并且在程序到达新代码段时增量地预编译和翻译这些新代码段。
一个简单的、增量式翻译过程工作如下:在源二进制代码被加载到内存中后,EM使用简单的译码-分派或间接线索方法开始解释二进制代码。随着它的进行,解释器动态地产生中间代码或翻译后的目标二进制代码。翻译代码被放到代码cache中,相应的SPC到TPC映射被放到映射表中。当遇到分支或跳转指令时,解释器就完成了一个动态基本快的翻译。接着,EM就沿着源程序的控制路径(使用映射表),或者在下一块已经被翻译(在表中有一个命中)时直接执行下一块,或者在下一块还没有翻译时开始翻译下一个动态基本块。逐渐地,程序的更多部分被发现和翻译,直到最终只执行翻译代码。
追踪源程序代码
控制转移优化
翻译链接
在这种方式中,每次翻译一个块,但是它们在构造时就被链接在一起形成链。后继翻译块的地址时通过SPC的值访问映射表,找出对应的TPC(如果相应的基本块已经被翻译)来确定的。如果后继块还没有被翻译,那么将插入标准的存根代码。在稍后的某一时刻,当后继块被翻译并且前驱块推出进到EM之后,EM将访问映射表,找出在表中的条目,取回后继块的TPC。
软件间接跳转预测
内联高速缓存,并非所有的映射都是通过hash查找来实现,最频繁使用的映射被单独提取出来。这种技术应该与profiling相结合,profiling提供关于间接跳转目标的精确信息。
影子栈
指令集问题
寄存器结构
条件码
惰性计算,它保存设置条件码的操作数和操作而不是条件码设置本身。这使得仅在需要的时候才生成所需的条件码。例如,可以维护一张惰性条件码表,它对每个条件码位建立一个表项。这个表项包含最近修改这个条件码位的指令的操作码、它的操作数和它的结果。不过,并不产生条件码本身。于是,如果随后的指令,如一个条件分支,需要使用一个或多个的条件码位,就会访问条件码表并产生所需的条件码。
数据格式和运算
内存地址解析
内存数据对齐
字节序
寻址地址