Description
Problem
Many current instructions and probably many future ones are based on load and store operations. I have noticed that the same code is repeated over and over again. Some examples are in Zcmp (#730), Zcmt (#757), Zilsd/Zcl (#765) and in extensions that are already implemented and lead to:
- Redundant code
- Higher maintenance burden
- Increased risk of inconsistencies when making changes
- High number of lines of code (In case code ends up in the specification)
Proposed solution
Create a generalized interface or template mechanism that can handle the common aspects of load/store operations.
This abstraction would accept common arguments, like AccessType, Aquire, Release etc.
Expected benefits
- Reduced code duplication
- Improved maintainability
- Easier implementation of new load/store variants
- More consistent behavior across related instructions
- Better testability of core load/store logic
As we also plan to integrate Sail Code into the specification we would also reduce the number of lines for all affected instructions (The generalized interface will be provided as part of the appendix)
Implementation approach
I thought of something like I did in Zcmt (#757) but only in better (return error cause + Retired Illegal, in case you want to take further action)
type target_address = bits(xlen)
union FJT_Result = {
FJT_Success : target_address,
FJT_Failure : unit
}
function fetch_jump_table(table_address : bits(xlen)) -> FJT_Result = {
/* Fetching jump table address needs execute permission */
match ext_data_get_addr_from_bits(table_address, Execute(), xlen_bytes) {
Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); FJT_Failure() },
Ext_DataAddr_OK(vaddr) => {
if check_misaligned(vaddr, size_bytes(xlen_bytes))
then { handle_mem_exception(vaddr, E_Load_Addr_Align()); FJT_Failure() }
else match translateAddr(vaddr, Execute()) {
TR_Failure(e, _) => { handle_mem_exception(vaddr, e); FJT_Failure() },
TR_Address(paddr, _) =>
match mem_read(Execute(), paddr, xlen_bytes, false, false, false) {
Ok(result) => { FJT_Success(result) },
Err(e) => { handle_mem_exception(vaddr, e); FJT_Failure() },
}
}
}
}
}
I would like to discuss the idea of generalizing load and store operations to reduce code redundancy, as many instructions essentially perform the same function. Before I start working on this, I would like to confirm whether this approach makes sense and if there is agreement.