在C++的功能性ISA模拟器上实现陷阱(异常/中断)

Implementation of traps(exceptions/intterupts) at functional ISA simulator at C++

本文关键字:陷阱 异常 中断 实现 C++ 功能性 ISA 模拟器      更新时间:2023-10-16

我尝试实现功能性ISA模拟器:目标是RISC-V和MIPS。它是一个循序渐进的指令解释器。

抽象步骤:

while(num_steps)
{
try
{
take_interrupt();// take pending interrupts
fetch(); // fetch instruction from memory
decode(); // find handler to instruction
execute(); // perform instruction
} 
catch (Trap& e)
{
take_trap(e); //configure appropriate system registers and jump to trap vector.
}
}

正如您所看到的,C++异常用于传输控制流。也许还有更帅气的设计?

问题:在功能ISA模拟器上实现陷阱的最佳方法/实践是什么。此外,我对翻译模拟器中的异常/陷阱实现感兴趣,比如QEMU。

注意:单词trap我指的是ISA定义的陷阱,而不是应用程序错误:内存访问错位、非法指令、系统寄存器访问故障、特权级别更改等。

QEMU使用C setjmp()/longjmp(。然后,该循环查看标志,并在继续执行来宾代码之前为"输入异常处理程序"设置CPU状态。

因此,我们使用C等价于抛出异常;正如NonNumeric所说,不需要实现这样的来宾异常(名称的巧合只是巧合)。但是,由于触发页面错误的内存访问是不常见的情况,因此长jmp或抛出C++异常比在所有内存访问代码路径中包含"处理失败返回"更有效。来宾内存访问是一个特殊的热点,QEMU通过一点自定义的内联程序集实现了其内存访问快速路径,因此我们关心在不执行longjmp的情况下退出到页面错误的顶层循环所需的额外少量指令。一个模拟器使用一个简单的"获取/解码/执行"循环,而不执行来宾代码的JIT,它不太关心性能,所以你的选择可能取决于对代码风格和可维护性的偏好。

QEMU是用C编写的,所以它不使用C++异常。您也不必通过C++异常来处理ISA陷阱。异常应该在对您作为实现者有用时使用,仅此而已。

还要注意,陷阱并不是特别的东西,它们仍然是模拟系统工作流程的一部分。像这样编码除法是完全合法的

if (reg[divisor] != 0)
reg[target] = reg[divident] / reg[divisor];
else
trap(TRAP_DIV0)

其中trap()函数直接更新架构状态,以便下一个要模拟的指令将来自异常处理程序。

void trap(int trap_id)
{
// save relevant registers according to platform spec
...
// set instruction pointer to trap handler start
reg[IP_INDEX] = trap_table[trap_id].ip;
// update other registers according to spec
...
}

C++异常可以让你的生活更轻松。例如,许多平台上的内存访问需要将虚拟地址转换为物理地址。此转换可能会导致陷阱(由于访问权限不足或配置错误)。写起来可能更容易:

void some_isa_instruction_handler()
{
int value1 = read_memory(address1);
int value2 = read_memory(address2);
int res = perform_something(value1, value2);
write_memory(address3, res);
}

当需要ISA陷阱时,CCD_ 2和CCD_。然后,take_trap()函数将回滚中断的指令处理程序执行的任何更改(如果需要),并设置执行以模仿陷阱处理程序,就像上面的trap()所做的那样。

模拟CISC系统可能会从这种风格中受益更多。