lb
lyszt.net covers lb and other RISC-V instructions in more detail.
Encoding format
Description
lb reads one byte from memory, which is the smallest unit a load can retrieve. Memory is built from bytes, each byte is 8 bits, and one address identifies one byte. lw reads 4 bytes, lh reads 2, and lb reads 1. It uses the same addressing as the other loads: a base register plus an offset (see the lw page). The form is shown below.
lb rd, offset(rs1)
A register holds 32 bits and a byte holds 8, so the top 24 bits need a value. lb sign-extends: it copies the top bit of the byte into all the upper bits, keeping the value as a signed number. A byte holding -5 arrives as a 32-bit -5.
The signed/unsigned choice matters most for text, the most common byte data, because character codes are unsigned. Many character codes have their top bit set. Loading one with the sign-extending lb produces a negative number, and comparisons against ordinary positive character codes then fail. Use lbu, load byte unsigned, for text; it fills the top bits with 0s. Use lb for signed data, such as small offsets that can be positive or negative.
The RISC-V manual defines lb and lbu as the 8-bit versions of the halfword loads, with the same effective address of register rs1 plus the sign-extended 12-bit offset. Memory is byte-addressed, so one address identifies one byte.
Bytes sit one address apart, so a loop moves through a byte array by adding 1 to the address. A typical loop reads a byte, checks whether it is zero (the marker for the end of text), acts on it, and advances the address by 1.
Example
.data val: .byte -5 .text la t0, val lb t1, 0(t0) # t1 = -5 (sign-extended)
