Project F

RISC-V Assembler: Shift

Published · Updated

In this post we look at RISC-V shift instructions, such as sll and srai. These instructions are included in RV32I, the base integer instruction set. New to the assembler series? Check out the first part on RISC-V arithmetic instructions.

In the last few years, we’ve seen an explosion of RISC-V CPU designs, especially on FPGA. Thankfully, RISC-V is ideal for assembly programming with its compact, easy-to-learn instruction set. This series will help you learn and understand 32-bit RISC-V instructions (RV32) and the RISC-V ABI.

Share your thoughts with @WillFlux on Mastodon or Twitter. If you like what I do, sponsor me. 🙏

Left Shift

There are two left shift instructions, sll and slli; they shift left, filling vacated bits with zeros. Left shift is almost the same as multiplying by powers of two, provided the number is positive.

sll  rd, rs1, rs2  # rd = rs1 << rs2
slli rd, rs, imm   # rd = rs1 << imm

ProTip: Remember shift instructions as initialisms: sll is shift left logical.

The following examples shift a register two bits to the left:

li   t0, 42      # t0 = (0000 0000 0000 0000 0000 0000 0010 1010)
li   t2, 2       # t2 = (0000 0000 0000 0000 0000 0000 0000 0010)

sll  t3, t0, t2  # t3 = (0000 0000 0000 0000 0000 0000 1010 1000)
slli t4, t0, 2   # t4 = (0000 0000 0000 0000 0000 0000 1010 1000)

Note how the result of shifting 42 two bits to the left is 168 (4×42).

Right Shift

There are four right shift instructions. srl and srli fill vacated bits with zeros. sra and srai perform sign extension, filling vacated bits with the most significant bit (MSB).

# shift right logical
srl  rd, rs1, rs2  # rd = rs1 >> rs2
srli rd, rs, imm   # rd = rs1 >> imm

# shift right arithmetic
sra  rd, rs1, rs2  # rd = rs1 >>> rs2
srai rd, rs, imm   # rd = rs1 >>> imm

Our right-shift examples use the immediate version of the instruction to compare logical and arithmetic shifts:

li  t0, 42      # t0 = (0000 0000 0000 0000 0000 0000 0010 1010)
li  t1, -42     # t1 = (1111 1111 1111 1111 1111 1111 1101 0110)

# right shift by 2 bits (MSB=0)
srli t2, t0, 2  # t2 = (0000 0000 0000 0000 0000 0000 0000 1010)
srai t3, t0, 2  # t3 = (0000 0000 0000 0000 0000 0000 0000 1010)

# right shift by 2 bits (MSB=1)
srli t4, t1, 2  # t4 = (0011 1111 1111 1111 1111 1111 1111 0101)
srai t5, t1, 2  # t5 = (1111 1111 1111 1111 1111 1111 1111 0101)

Both signed and unsigned numbers are positive when the MSB (most significant bit) is 0. So, both right shift instructions fill vacated bits with 0.

When the MSB is 1, signed numbers are negative. Arithmetic shift fills vacated bits with 1, while logical shifts fill them with 0. The results are dramatically different! Treating the result as a signed number:

  • t4 = 1,073,741,813
  • t5 = -11

In summary, consider whether a value is signed before choosing a right shift instruction.

Learn more about the representation of signed numbers from two’s complement on Wikipedia.

Shift Bits

RV32 shift instructions use the 5 least significant bits for the shift amount (0-31); other bits are ignored. For example, left shifting by 32 does nothing (you might expect it to set the register to zero).

GNU assembler errors if you try to shift by an immediate not in the range 0-31: Error: improper shift amount (32).

What’s Next?

The next instalment of RISC-V Assembler covers Load and Store Instructions, including memory alignment, addressing modes, and loading symbol addresses.

Other parts of this series include: Arithmetic, Logical, and Branch Set. Or check out my FPGA & RISC-V Tutorials and my series on early Macintosh History.

References