This page introduces the basic structure needed to implement a for-loop in RISC-V. Consider the following for loop in a higher level language:
for (int i = 0; i < n; i = i + 1) {
total = total + i * i;
}
In particular, there are four components of the loop's control flow we want to address:
initialization, the loop condition, update statement, and the body of the loop. The initialization code
should only be run once before the loop begins, and in our case we are initializing i = 0
. The loop condition runs before every iteration checking if it should continue looping or not, and the update step runs at the end
of every iteration which updates something, and in our case increasing i
by 1. Finally, between the loop condition and the update step, we have the body of the loop. The complete control flow
for our loop is shown in the figure below.
Similar to while-loops (04b
), we will have a label and guard branch at the start of the loop, and a branch at
the end of the body of the loop, checking if we should continue. In addition to this we will have some initialization instructions before the loop and an instruction incrementing i
.
# Register usage:
# t0: i
# t1: n
# t2: total
# t3: Intermediary for calculations
# Start of for loop.
addi t0, zero, 0 # i = 0
bge t0, t1, _joinPoint # If i >= n, our condition is false
_forLoop:
mul t3, t0, t0 # t3 <- i * i
add t2, t2, t3 # total = total + i * i
# Now we increment i and loop.
addi t0, t0, 1 # i = i + 1
blt t0, t1, _forLoop # Checking if i < n.
_joinPoint:
# Code after loop.
# ...
While the above code does indeed work, it does not give us much room to modify it as we see fit.
We will add in a new label _forLoopNext
which signifies that the body of the loop
is done, and we should both perform the update step and check to see if we should
continue looping.
_forLoop:
# Body of for loop.
# ...
_forLoopNext:
# Increment i and check condition.
addi t0, t0, 1
blt t0, t1, _forLoop
This achieves two things: first, it gives an explicit place to perform the
necessary steps for the loop making it easier to read and debug, and second it allows us to include (the equivalent of) a continue
statement in our
loop. If we want to execute the continue
statement at any point, simply jump to
the _forLoopNext
label. (e.g. jal zero, _forLoopNext
).
In addition, we can also jump to _joinPoint
as a break
statement.
# Start of for loop.
addi t0, zero, 0 # i = 0
bge t0, t1, _joinPoint # If i >= n, our condition is false
_forLoop:
mul t3, t0, t0 # t3 <- i * i
add t2, t2, t3 # total = total + i * i
_forLoopNext:
# Now we increment i and loop.
addi t0, t0, 1 # i = i + 1
blt t0, t1, _forLoop # Checking if i < n.
_joinPoint:
# Code after loop.
# ...
A link to a full implementation of the above is below.
It is worth emphasizing that this is just an example of a for-loop in RISC-V using a guard branch. For a for loop without one, we can make a similar modification as with while-loops. On top of that, for simpler loops, we can exclude labels entirely if they are redundant.