# RISC-V Assembler: Shift

This RISC-V assembler post covers shift instructions, such as **sll**, **srl**, and **srai**. I also explain how to use shift instructions to quickly multiply and divide by powers of two. Shift instructions are included in RV32I, the base integer instruction set.

In the last few years, we’ve seen an explosion of RISC-V CPU designs on FPGA and ASIC, including the RP2350 found on the Raspberry Pi Pico 2. 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 and programming.

**RISC-V Assembler Series**: Arithmetic | Logical | Shift | Load and Store | Branch and Set | Jump and Function | Multiply and Divide | Assembler Cheat Sheet

## Left Shift

There are two left-shift instructions, **sll** and **slli**; they shift left, filling vacated bits with zeros.

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

ProTip: Remember shift instructions as initialisms: **sll** is **s**hift **l**eft **l**ogical.

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).

### Shift Multiplier

Left shift is equivalent to multiplying by powers of two. Using left shift for multiplication is often faster on small CPUs, particularly those without the **mul** instruction.

For example, shifting left by three bits multiplies by 8 because 2^{3} = 8:

```
li t0, 4 # t0 = 4
slli t1, t0, 3 # t1 = 4 << 3 = 32
li t0, -4 # t0 = -4
slli t1, t0, 3 # t1 = -4 << 3 = -32
```

See Multiply Divide for flexible multiplication with **mul**.

## 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, rs1, imm # rd = rs1 >> imm
# shift right arithmetic
sra rd, rs1, rs2 # rd = rs1 >>> rs2
srai rd, rs1, imm # rd = rs1 >>> imm
```

These right-shift examples use immediate instructions to contrast 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`

Always 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 Divider

Right shift is (almost) equivalent to dividing by powers of two. You can divide signed and unsigned numbers by choosing the correct instruction: **srli** for unsigned and **srai** for signed.

For example, shifting right by three bits divides by 8 because 2^{3} = 8:

```
li t0, 42 # t0 = 42
srli t1, t0, 3 # t1 = 42 >> 3 = 5 ; logical shift for unsigned division
li t2, -42 # t2 = -42
srai t3, t2, 3 # t3 = -42 >>> 3 = -6 ; arithmetic shift for signed division
```

Division is typically slow, even on CPUs with the **div** instruction. Right shift can significantly increase performance when dividing by powers of two. However, there is a difference: **div** always rounds towards zero, whereas **srai** rounds towards negative infinity. You have been warned! 😅

See Multiply Divide for flexible division with **div** and **rem**.

## 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 post looks at RISC-V **Load and Store Instructions**, including addressing modes.

Check out the RISC-V Assembler Cheat Sheet and all my FPGA & RISC-V Tutorials.

Share your thoughts with me on Mastodon or X. If you enjoy my work, please sponsor me. Sponsors help me create new projects for everyone, *and* they get early access to blog posts and source code. 🙏

### References

- RISC-V Technical Specifications (riscv.org)