Introduction
A jump table in RISC-V assembly is an efficient way to implement
multi-way branching (like a switch statement in C). Instead of
executing a long chain of conditional branches, you store a list
of target memory addresses in an array and use an index to load and
jump directly to the correct destination.
In RISC-V, this is achieved using the jalr (Jump and Link Register)
instruction, which allows jumping to an address calculated inside a register.
Code
This an implementation of a jump table for a basic calculator module
(0 for Add, 1 for Subtract, 2 for Multiply).
# jump table demonstration code
.data
# ---- jump table containing target memory addresses
.align 2
jump_table:
.word do_add
.word do_sub
.word do_mul
.text
.globl main
main:
# ---- example inputs
li t0, 1 # t0 = index (e.g., 1 for subtract)
li a0, 20 # a0 = operand A
li a1, 5 # a1 = operand B
# ---- bounds check (crucial! ensures index is between 0 and 2)
li t1, 3
bltu t0, t1, valid # If index < 3, proceed
j default_case # Else, go to fallback
valid:
# ---- calculate address offset (index * 4 bytes for 32-bit words)
slli t0, t0, 2 # t0 = index * 4
# ---- load the base address of the jump table
la t2, jump_table # t2 = base address of jump_table
# ---- get the target address from the table
add t2, t2, t0 # t2 = jump_table + (index * 4)
lw t3, 0(t2) # t3 = target address (e.g., address of do_sub)
# ---- jump to the target address
jr t3 # Unconditional jump to register address
# (pseudo-op for jalr x0, 0(t3))
# --- branch targets
do_add:
add a2, a0, a1 # a2 = 20 + 5 = 25
j end
do_sub:
sub a2, a0, a1 # a2 = 20 - 5 = 15
j end
do_mul:
mul a2, a0, a1 # a2 = 20 * 5 = 100
j end
default_case:
li a2, -1 # Return -1 for invalid index
end:
# ---- result is now in a2
Code found on the web. Untested.
Breakdown of Steps
- Table Structure (.word): The jump table sits in the data segment
as an array of pointers to labels.
- Bounds Checking (bltu): Using Branch Less Than Unsigned (bltu) serves
a double purpose. It catches indexes that are too high, and automatically
catches negative inputs (since a negative integer evaluates to a massive
unsigned integer).
- Index Shifting (slli): Because addresses are word-aligned, each index
corresponds to a 4-byte offset in a 32-bit architecture (RV32). Shift the
index left by 2 bits (slli index, index, 2) to multiply by 4.
Note: For a 64-bit architecture (RV64), use .dword pointers, and shift
left by 3 bits (slli index, index, 3) to multiply by 8.
- Indirect Jump (jr / jalr): The jr t3 instruction changes the program
counter (PC) directly to the value residing in t3. Because it passes x0
to the hardware layer underneath, it performs the jump without saving a
return address.