jal
uses a semi-absolute target encoding (replacing the low 28 bits of PC), while bgezal
/ bltzal
are relative (adding an 18-bit signed displacement, imm16<<2
). How to Calculate Jump Target Address and Branch Target Address?
They are classic MIPS's only branch-and-link (instead of jump-and-link), so are important for position-independent relocatable code. (You can even use one to get the current PC into a register and find out where you're executing from, unlike with jal
).
You can encode bal
(unconditional relative function call) as bgezal $zero, target
.
You can get $ra=PC
with a not-taken bltzal $zero, anywhere
without needing any other setup. Doing that with bgezal
would need a less-than-zero input register that would take an insn to create. b...al
instructions always write $ra
even if the branch is not taken. You want this for PC-relative code, until MIPS32r6 gave us addiupc
for better PC-relative address generation.
Since they use an I-type instruction format like other b
ranch instructions, there's room in the encoding for one register, so it made sense to make it optionally conditional instead of just having a bal
instruction. The hardware logic for doing the "and link" was already there, and all the other relative branch instructions are conditional. Plus, having a condition that's not-taken for $zero
could be convenient for reading pc
.
Remember that MIPS instruction encodings were used directly as internal control signals in early MIPS hardware, so the one bit in the encoding that differs between them probably gets wired to an XOR gate that inverts (or not) the check on the sign bit. (As Konrad's answer points out, these branch conditions depend only on the MSB of the register because it's always against zero, so there's no latency of waiting for a 32-bit adder to produce a compare result.)
From http://www.mrc.uidaho.edu/mrc/people/jff/digital/MIPSir.html
0000 01ss sss1 0001 iiii iiii iiii iiii BGEZAL
0000 01ss sss1 0000 iiii iiii iiii iiii BLTZAL
This lack of flexibility in instruction-encoding (because it directly drove internal control signals instead of needing much transformation in decoding) is perhaps why there isn't just a single bal
with a 28-bit range (from a 26-bit relative displacement). The hardware for relative branches was set up for I-type instructions with 16-bit immediates.
TL:DR: there are 2 conditional branch-and-link instructions because it was natural to implement an unconditional bal
in terms of one of them, and the other came along nearly for free.
MIPS b
(unconditional relative branch without link) is also a pseudo-instruction for beq $zero, $zero, target
, or at the assembler's choice, for bgez $zero, target
. (What is the difference between unconditional branch and unconditional jump (instructions in MIPS)?). The MIPS R3000 manual suggests beq $zero,$zero
. (And more clearly documents that $ra=PC
happens regardless of branching; That wasn't clear from the quick-reference sheets I was looking at while writing this answer originally.)
The compare-to-zero encodings only have one 5-bit register field, so they consume less coding space than beq
/ bne
. That's a likely reason for choosing bgezal
rather than beqal
as one of the pair of conditional branches to provide.