lh
lyszt.net covers lh and other RISC-V instructions in more detail.
Encoding format
Description
lh loads a halfword from memory. A byte is 8 bits and holds its own address, which makes it the smallest addressable unit. A word is 4 bytes, or 32 bits, and a halfword is half of that: 2 bytes, or 16 bits. lh reads 16 bits where lw reads 32. The addressing matches lw: a base register plus a constant offset (see the lw page). The form appears below.
lh rd, offset(rs1)
A register holds 32 bits, so a 16-bit load must fill the remaining 16 bits. lh uses sign extension. It reads the top bit of the 16-bit value, which marks the sign of a signed number, and copies that bit into all 16 upper bits of the register. The value keeps its sign, so a 16-bit -3 arrives as a 32-bit -3.
Use lh for signed 16-bit data, such as audio samples or sensor readings that can go negative. Its partner lhu, with u for unsigned, fills the top with 0s, which suits data that never goes negative. The bit pattern 0x8000 loaded with lh becomes -32768, and loaded with lhu it becomes +32768. The bits in memory stay the same, and the load decides the meaning.
The RISC-V manual defines lh as loading a 16-bit value from memory and sign-extending it to 32 bits before storing it in rd. The effective address is register rs1 plus the sign-extended 12-bit offset. An access is naturally aligned when its address is divisible by the size of the access in bytes, so a halfword access is aligned at even addresses.
Halfwords are 2 bytes, so arrays of them sit at offsets 0, 2, 4, and a pointer moves through them by adding 2.
Example
.data val: .half -5 .text la t0, val lh t1, 0(t0) # t1 = -5 (sign-extended)
