Pre-test Loops in Assembly
note
The Assembly language discussed in this lesson refers specifically to ARMv8 AArch64 Assembly.
prerequisite
Before starting this lesson, you should have a basic understanding of the following prerequisite lessons:
There are times when you need to use a pre-test loop to repeatedly execute a set of instructions or iterate through a collection of elements, such as an array. A common implementation of a pre-test loop is the for loop or the while-loop. This lesson covers how pre-test loops can be implemented in Assembly.
The Definition
A pre-test loop is a type of loop in which the loop condition is evaluated before each iteration, including the first. In other words, the condition must be evaluated and satisfied before the loop body is executed. Consequently, a pre-test loop may execute zero or more times, as the loop body is skipped entirely if the condition is false during the initial evaluation.
The implementation of this type of loop in high-level programming languages is commonly achieved using for loops or while loops, in which the loop condition is evaluated before each iteration of the loop body. The following code demonstrates an example of a pre-test loop implemented in C.
#include <stdio.h>int main() { // for loop implementation in C // Printing the numbers 1 to 5 printf("Pre-test loop implementation\n"); for (int i = 1; i <= 5; i++) { printf("%d\n", i); } printf("Loop finished\n"); return 0;}#include <stdio.h>int main() { // while loop implementation // Printing the numbers 1 to 5 printf("Pre-test loop implementation\n"); int i = 1; while (i <= 5) { printf("%d\n", i); i++; } printf("Loop finished\n"); return 0;}The above loop in C produces the same output of printing the numbers 1 to 5, with the only difference of one being a for loop and the other being a while loop.
note
The term pre-conditional loop refers to a pre-test loop. These terminologies are generally used interchangeably because both refer to a loop where the condition is evaluated before the loop body executes.
The Logic Behind It
Based on the observations from the loops above, both loops produce the same output, and their underlying logic can be simplified as follows:
// PseudocodePRINT "Pre-test loop implementation\n"DECLARE i AS INTEGERi = 1IF i <= 5 THEN PRINT i i = i + 1 JUMP BACK TO THE IF CHECKPRINT "Loop Finished"The logic presented above closely resembles the branching mechanism used in Assembly and provides the basic control flow needed to implement a pre-test loop. The process of implementing a pre-test loop can therefore be summarised as follows:
-
Initialise the loop variable.
-
State and check the loop condition.
-
Execute the loop body if the condition is satisfied.
-
Branch back to the condition check.
-
Exit the loop when the condition is no longer satisfied.
note
It can be observed that the logic more closely resembles a while loop. There is no direct one-to-one equivalent of a for loop or a while loop in Assembly. Instead, Assembly implementations reproduce the control flow and behaviour of these high-level loop constructs. Therefore, when implementing a for loop in Assembly, it can be helpful to first express it in the form of a while loop. This provides a clearer representation of the loop's control flow and makes it easier to translate into Assembly instructions.
The Assembly Implementation
The above C code and pseudocode can be implemented in Assembly as follows:
// Pseudo-ops and directives...// string1 = "Pre-test loop implementation\n"// string2 = "%d\n"// string3 = "Loop finished\n"main: // Program prologue ... // Printing the "Pre-test loop implementation" String ldr x0, =string1 bl printf // Initialising the Loop variable (i) mov x20, 1 b loopcond // Branching to the loop conditionloopbody: ldr x0, =string2 // loading the format string mov x1, x20 // loading the i to argument register bl printf add x20, x20, 1 // Incrementing i by one // x20 = x20 + 1loopcond: cmp x20, 5 // Comparing i with 5 b.le loopbody // if i is less than or equal to 5, branch to loop body // otherwise code below is executed ldr x0, =string3 bl printf // program epilogue.The Assembly code above implements a pre-test loop that prints the values of i from 1 to 5. The loop variable (i) is stored in register x20, while x0 and x1 are used to pass arguments to printf.
The program first prints the introductory string and then initialises x20 to 1, representing the initial value of i. The b loopcond instruction then transfers execution to the loop condition before the loop body is executed.
At loopcond, the cmp instruction compares the value in x20 with 5. The b.le loopbody instruction checks the result of this comparison. If x20 is less than or equal to 5, execution branches to loopbody. Otherwise, the branch is not taken and execution continues to the instructions following the loop.
Inside loopbody, the current value of x20 is passed to printf and printed. The add instruction then increments x20 by 1. After the loop body finishes, execution naturally continues to the loopcond label, where the condition is checked again.
This process continues until x20 becomes greater than 5. At that point, b.le loopbody is not taken, so execution continues to the code after the loop, which prints "Loop finished".
note
The loop condition is checked before the execution of the loop body. The condition is evaluated using the cmp instruction and the associated conditional branch instruction. In this implementation, the loopcond label represents the loop condition, while the loopbody label represents the loop body.
It is also important to note that the loop condition is placed after the loop body in the Assembly code. This approach takes advantage of Assembly's default downward flow of execution. After the loop body is executed, execution naturally continues to the loop condition without requiring an additional branch from the loop body back to the condition. An initial branch to loopcond is still required to start the loop. However, this approach avoids requiring an additional branch at the end of every iteration, making the control flow more efficient.
Putting it All Together
The following code demonstrates the complete process of implementing a pre-test loop in Assembly, including the pseudo-ops, program prologue, and epilogue.