跳转至

主动修复机制

主动修复(Fixup)属性赋予了块指令自行处理异常的能力。这种自行处理异常的能力可以理解为,如果一个块内触发的了fixup,那么块就会在异常指令上中止执行跳转到fixup指定的目标块继续执行,而不需要陷入到更高的特权级进行异常处理。通过这种处理方式,软件可以灵活的设计程序,针对一些特定的异常自行安排处理的代码,避免发生异常时上下文保存和恢复以及特权级切换带来的开销。

当前版本下,可以通过主动修复Fixup方式处理的异常包括:

  • 内存访问异常:例如:内核中unaccess场景;内存读写过程中发现地址不对齐等
  • 断言失败:通过系统块内微指令assert触发。

主动修复块表明了代码对异常的预见性,针对程序中可能出现的一些异常提前指定统一的异常处理的代码。

Fixup执行

一个块执行过程中触发了相应的异常时,是否允许执行fixup的流程受控于系统寄存器FUTO_ACRn。如果FUTO_ACRn寄存器没有定义主动修复接管该类异常,那么则允许正常执行fixup的流程。否则,执行常规的异常处理流程。

fixup执行示意图如下:

fixup

  1. 块指令在发生异常的指令上中止执行并立即提交;
  2. 跳转到fixup指示的目标块继续执行。

但如果主动修复块所在特权级希望代理这种异常,仍可以通过系统寄存器FUTO_ACRn接管该异常。这种情况下,不会执行fixup指示的目标块,发生异常时便会被当前所在特权级的异常处理流程接管。

汇编示例

主动修复块主要用于简化参数检查的成本,示例如下:

    # 假定本块的入口参数如下:
    # a0 - 数组地址; a1 - 数组下标
    BSTART.STD FALL, .bar          # .bar 
    cmp.lti a1, ARRAY_SIZE, ->t    # 检查a1的范围
    assert t#1                     # 如果assert失败,则跳转到.bar位置
    ld [a0, a1<<3],         ->t    # 直接开始使用数组
    ...                            

    BSTART.STD RET
    add zero, 0,           ->a0    # 返回成功
    setc.tgt ra

.bar: 
    BSTART.STD RET
    add zero, 1,           ->a0    # 返回错误
    setc.tgt ra
    ...
    BSTOP

在这个例子中,代码本来需要检查a0的地址是否有效,也需要检查a1的范围是否越界,但通过定义块的主动修复属性,块内不需要检查地址的有效性,对于数据的范围,也不需要创建额外的处理分支,直接使用比较指令assert进行范围检查。在大部分正常流程中,代码可以顺序执行。仅在特殊的流程中,异常的流程才会触发,进入异常流程(即例子中的.bar)进行特殊的处理。