RISC-V Memory Instructions
The following are the storing/loading instructions in 32-bit RISC-V. If the arguments of an instruction are not defined, then they are equivalent to the arguments of the first instruction with defined arguments. Instructions in italic are pseudoinstructions:
| Operation | Description |
| :-------: | :----------------------------------------------------------------------------------: |
| lw(u) rd imm(rs1) | Loads a 4-byte “word” of memory into the destination registerrdfrom addressrs1 + imm. |
| lb(u) | Loads 1 byte of memory into the destination registerrdfrom addressrs1 + imm. |
| lh(u) | Loads a 2-byte “half word” of memory into the destination registerrdfrom addressrs1 + imm. |
sw rs2 imm(rs1) | Stores inrs1 + imma 4-byte “word” address fromrs2. |
| sb | Stores inrs2a 1-byte address starting atrs1 + immin memory. |
| sh | Stores inrs2a 2-byte “half word” address starting atrs1 + immin memory. |
| lui rd imm | Absolute addressing: loads a 20-bit “upper” immediate with the last 12 bits set to 0 (shifted left by 12 bits) intord(rd = imm << 12). |
| auipc | Relative addressing: adds a 20-bit upper immediate to the current address of the program counter and setsrdto the immediate relative to the program counter (rd = (imm << 12) + PC). |
| li rd imm | Sets the registerrdto a 32-bit immediate value. |
| li rd label | Sets the registerrdto the 32-bit address oflabel. |Note! imm value must be equal to the amount of bytes (and not pointers/elements) you want to shift rs1 by.
Your
Intuition
Let’s talk about the elephant in the room here: lui and auipc. What the heck do these do? Well, they are in fact custom instructions simply made for the li and la pseudoinstructions. Consider the li t0 0x12345678 pseudoinstruction, with a pretty long address. It can be written as:
addi t0, x0, 0x678 // t0 = 0x678
slli t0, t0, 12 // t0 = 0x678 << 12 = 0x678000
addi t0, t0, 0x345 // t0 = 0x678000 + 0x345 = 0x678345
slli t0, t0, 12 // t0 = 0x678345 << 12 = 0x678345000
addi t0, t0, 0x12 // t0 = 0x678345000 + 0x12 = 0x12345678
However, that is pretty inefficient, right? Therefore we need to find a more efficient method to store big immediates. Enter lui.
As stated above, lui rd 0x12345 stores the most significant 20 bits into the 32-bit address of rd. However, 0x12345678 is a 32-bit string! Turns out that addi rd 0x678 can simply do the rest. Then, we get rd = 0x12345000 + 0x678.
The pseudoinstruction la follows a similar approach, but with auipc as its first instruction. Consider la t0 label where the address of label is 0x12345.
- The
auipcinstruction takes the current PC value,0x1000(for example). - It shifts the immediate
0x12345left by 12 bits to get0x12345000. - It adds the current PC value
0x1000to this shifted immediate value to get0x12346000. - The result
0x12346000is stored in registert0.