SBI: We can fast handle some SBI functions for extreme performance in assembly code implementation if SBI extension‘s FID equals to zero

洛佳 Luo Jia

RISC-V SBI provides platform agnostic functions for kernels. The nomal handling procedure in an SBI implementation would include context switch and call higher language code, e.g. Rust or C code. However when SBI function has FID=0 (no matter what extension module EID is), we can figure out another way to provide such SBI functions without context switch. The assembly code would write as follows:
unsafe fn trap_begin() {
        "bnez a6, 1f", // set_timer EID(a7): 0x54494D45, FID(a6): 0
        "li   a6, 0x54494D45",
        "bne  a6, a7, 1f",
        "csrr a6, mcause",
        "li   a7, 9",
        "bne  a6, a7, 1f", // if mcause != supervisor ecall, jump to conventional way of handling
        "li   a6, 0x200bff8", // CLINT mtimecmp address (if device tree match this address, use trap_begin as mtvec, otherwise don't use it then it would be performance loss only but still correct)
        "sd   a0, 0(a6)", // a0: stime_value
        "mret", // return to supervisor without context restore
"csrrw sp, mscratch, sp",
"sd ra, 0(sp)", // ...context save... "call {rust_trap}",
"ld ra, 0(sp)", // ...context restore...
"csrrw sp, mscratch, sp", "mret" } }
The core idea of this assembly code is that the condition of entry of certain SBI function (in this example, set_timer) can be concluded as: mcause == 9 && EID == 0x54494D45 && FID == 0. Such comparison of register equals a constant non-zero value requires another register to store the constant value; but equals zero does not, because RISC-V provides the `zero` register where we can compare to, so `bnez` would run without any auxiliary registers to store constant. The arithmetic condition `&&` allows to switch equal comparisons in math, usually we compare `mcause` first, but comparing FID first is also correct in arithmetic result. Then in this way after FID == 0 is compared, we can compare EID and following mcause as well, using the `a6` register formally as a temporary register storing the FID value.

In this way we can accelerate such SBI calls faster, as only few assembly code is run, no context switch and higher programming language calls is required. But such ’irregular‘ way only come into effect if any comparison requires Value == 0, in SBI it would be FID == 0 (EID == 0 means a legacy module).

In future SBI extensions and vendor defined extensions, it might be better if we suggest any function that requires extreme performance or is called freqently has an FID that equals zero. The current SBI 1.0-rc extensions has already defined most performance required functions (FENCE.I ipi, set_timer, etc.) as FID == 0. If it's possible, we can set a rule or a formal advice in SBI standard that performance functions in extensions should be best to define as FID == 0; or if any SBI module includes only one function, the function's FID should best be FID == 0 to help the implementations to improve SBI call performace.

Join { to automatically receive all group messages.