beq
lyszt.net covers beq and other RISC-V instructions in more detail.
Encoding format
Description
beq lets a program make a decision. The processor normally executes instructions in the order they appear in the list. That order by itself cannot choose or repeat anything. A branch changes the order. It tests a condition, and if the condition holds, the processor continues at a different place instead of the next line. All if statements and loops are built from branches.
The different place is named by a label, a name followed by a colon that marks a position in the code, such as the label done in the example. beq (branch if equal) compares two registers. When they hold the same value, it jumps to the labeled position; when they differ, execution continues with the next instruction. The form appears below.
beq rs1, rs2, label
beq only changes which instruction runs next. Storing the yes or no result of a comparison as a number requires a different instruction, described on the slt page.
The processor tracks its position with a register called the program counter, which holds the address of the instruction that is running. A taken branch changes that address, so the next instruction comes from the label.
The RISC-V manual states that a conditional branch stores its offset as a 12-bit value counted in multiples of 2 bytes. The offset is sign-extended and added to the address of the branch instruction, giving a range of plus or minus 4 KiB, where a KiB is 1024 bytes. Branch instructions compare two registers directly and use no condition codes. The manual gives the reason that a combined compare-and-branch instruction fits into a regular pipeline and needs no extra state or temporary register. It also advises software to keep the most common path as straight code and to expect backward branches to be predicted as taken and forward branches as not taken, at least the first time they are encountered.
A branch compares two registers and cannot take a constant written in the instruction, so a form like beq t0, 5, label does not exist. To compare a register with a constant, load the constant into a register first with li, then compare the two registers.
li t1, 5 beq t0, t1, done
The value 0 needs no load, because the register zero always holds it.
A common use compares a register with zero, the register that always holds 0, to ask whether a value has reached zero. This ends countdown loops and checks results. The example branches to done when a0 has counted down to zero. beq and labels are enough to build every decision and loop in a program.
Example
beq a0, zero, done # if a0 == 0, jump to 'done'
