When designing an ever evolving ISA it may be difficult to ensure forward compatibility of implementations: if a new instruction is introduced it can not be used in programs that may run on older implementations as those implementation may (should) trap on what they consider as an illegal instruction.
This slows down the adoption of new extension. Most often, this cannot be avoided. If an implementation does not support an instruction it cannot execute the program which contains such instruction correctly. However in a few cases, when the instruction brings some extra functionality not required for correct program execution, it may be tempting to workaround the limitation of forward compatibility: use an instruction encoding which will be seen as a placeholder by some implementations (its execution won’t have much effect) and the same encoding will be decoded as a fleshed out operation by other implementations.
At least two versions of this workaround exist in RISC-V: one for hints and a new one being introduced in Zimop
/ Zcmop
. This blog post covers both of those type of placeholder encodings.
HINT instruction in RISC-V ISA
RISC-V defines a set of HINT instructions whose goal is to provide hints to the executing micro-architecture. The goals of those hints is to help the micro-architecture run the program more efficiently. For example hints can be used to indicated which memory block is expected to be accessed later or never to be accessed again (temporality hint) leaving the micro-architecture the opportunity to tune the allocation in its cache hierarchy accordingly.
In RISC-V, the HINT set is built on the idea of retargeting instructions without actual effect in the older extensions. For example a load immediate targeting x0, lui x0, <n>
, is part of the set of HINT instructions. Since this instruction has no architectural effect (x0 cannot be written) it can be used to indicate something to the micro-architecture. If the micro-architecture knows how to interpret the hint it can leveraged it, if not it can just drop the instruction (consider it a s N(O)OP, No Operation). Both solutions lead to a correct program execution, assuming the latter implementation does not consider the hint as reserved instruction.
HINT reserved instruction space is specified in Section 2.9 of RISC-V unprivileged specification (pdf).
At least three extensions defining some hint instructions have already been ratified: Zicbop, Zihintpause and Zihintntl.
Zicbop (spec) defines new instructions: prefetch.[irw]
, which can be used to indicate that software intends to perform a specific memory operation (data cache block read or write access, instruction cache block fetch access) in the near future.
Zihintntl (spec) defines new instructions: ntl
, which can be used to indicate poor locality of the next (target) instructions. The ntl
instructions are all encoded as add x0, x0, [x2-x5]
.
Zihintpause (spec) defines a new instruction: pause
, which can be used to indicate that the program execution could be beneficially paused for a bounded (possibly zero) amount of time.
May-be-ops: placeholders for Control Flow Integrity
Another variant of forward compatible instruction is in development at RVIA: Zimop and Zcmop. Zimop is an extension specifying 32-bit wide may-be-operations (maybe ops) and Zcmop is an extension defining 16-bit wide maybe ops.
Those extensions do not define instructions which are designed to be useful right away but instructions which can later be used as a placeholder. On one hand they are executable without fault and without changing program functionality by implementations which do not support any specification but the may-be-ops. On the other hand they can be used to implement specific functionalities leveraged by more recent implementations (one of the target application is control flow integrity).
Zimop
Zimop introduces the following 40 new instructions:
mop.r.n rd
withn
an integer taking any value from 0 to 31mop.rr.n rd
withn
an integer taking any value from 0 to 7
By default those instructions simply write the value 0 to XRF[rd]
.
Those instructions are specified such that they can be redefined by future extensions with a different behavior. The encoding of mop.r.n
allows it to read from a single source rs1
and the encoding of mop.rr.n
allows it to do the same but for two sources rs1
and rs2
. Moreover they can write rd
with a different value in the future.
The following diagram provide two examples of execution of a program containing mop.r.17 rd, rs1
followed by a branch on the value of rd
. In the left hand side case, the default Zimop behavior is implemented, the may-be-op has no effect except writing the value 0
to rd
, the branch is never taken. On the right hand side, the behavior of mop.r.17
has been overloaded (by a yet to be defined extension). Register rs1
which was encoded in the instruction and unused by the default implementation can be read and used by the overload, alongside the processor state, to determine a value for rd
.
Hypothetic example:
rd
could be set not to a non-zero value if the Floating-Point invalid flag is set. Allowing to branch on this case without explicitly reading the CSR only if this overload ofmop.r.17
is implemented.
Note: Reading CSR state is a possible behavior of may-be-op overloaded, explicitly listed in Zimop specification.
Zcmop
Zcmop introduces 8 new compressed1 instructions: c.mop.n
with n
an odd integer taking any value from 1 to 15. As specified in Zcmop, those instructions do not read nor write any register . Their encoding allows future redefinition of those instructions to read xm
where m=2n+1
. They are expected to be expandable to corresponding Zimop instructions. The expansion is left to the extension that redefines the May-Be-Operations. The fact that c.mop.n
does not write any register can be translated into a Zimop instruction writing x0
.
Conclusion
The two new extensions Zimop and Zcmop can seem a bit underwhelming: they define new instructions which do nothing useful: they simply write 0 to a destination register. As a Bay Area venture capitalist would say, it is not what they can do know which matters but what they will be able to do in the future. Zimop and Zcmop, especially if they are integrated as mandatory in a future profile will allow the future addition of extensions which bring specific features not mandatory to the correct execution of a program (e.g. control flow integrity) to be defined on top of those placeholder and to execute correctly (although without the added functionality) on any implementation which implements Zimop and Zcmop.
References
Initial suggestion for may-be-operations open by Ved Shanbhogue (Rivos) https://github.com/riscv/riscv-cfi/issues/131
Zimop draft specification source (riscv-isa-manual github repo)
compressed: 16-bit wide opcodes