AArch64 Playground
4.12 · Post-test loops

Post-test loops

Unlike a pre-test loop, where the condition is checked before the loop body runs, a post-test loop runs the loop body first and checks the condition afterward. This means that even if the condition is false from the start, the loop body still runs once. This lesson builds one in assembly and then runs it beside a pre-test loop so the difference shows.

What a post-test loop is

A post-test loop is a type of loop in which the loop condition is checked after each pass through the body, including the first. The body therefore runs at least once, even when the condition is false from the start.

In C, a post-test loop is written as a do-while loop, which checks its condition after each pass through the loop body. The following C code starts i at 6, already past the limit of 5:

#include <stdio.h>int main() {    // do-while loop implementation    // i starts past the limit, yet the body runs once    printf("Post-test loop implementation\n");    int i = 6;    do {        printf("%d\n", i);        i++;    } while (i <= 5);    printf("Loop finished\n");    return 0;}

The loop above prints 6 once. The test i <= 5 is false the first time it is checked, but by then the body has already run. The same loop written as a while loop, which tests first, prints nothing between its two lines. When i starts at 1, the two kinds of loop print the same numbers, 1 to 5: they only differ when the condition is false on the very first check.

note

The term post-conditional loop refers to a post-test loop. The two terms are interchangeable.

The logic behind it

The underlying logic is:

PRINT "Post-test loop implementation"DECLARE i AS INTEGERi = 6PRINT ii = i + 1IF i <= 5 THEN    JUMP BACK TO PRINT iPRINT "Loop finished"

The difference from a pre-test loop is when the condition is checked: here the body runs first.

The pre-test version opens with a branch to the loop condition. Remove that branch and execution falls straight into the body.

The assembly implementation

The C code and pseudocode above can be written in assembly as follows:

        ldr     x0, =fmt_head        bl      printf        mov     i_r, 6                          // i = 6: the test is false from the start        // no branch to the test, so the body runs firstloopbody:        ldr     x0, =fmt_i        mov     w1, i_r                         // pass i to printf        bl      printf        add     i_r, i_r, 1                     // i = i + 1loopcond:        cmp     i_r, 5        b.le    loopbody                        // go again while i <= 5        ldr     x0, =fmt_done        bl      printf

The instructions match the pre-test version: set the loop variable, run the body, add one, check the condition. Only the opening b loopcond is gone, so nothing is tested until the body has run once. With i at 6, the body prints 6, i becomes 7, the check 7 <= 5 fails, and the loop ends after one pass.

The whole program

The following code is the complete post-test loop program, including the data section, the prologue, and the epilogue. It prints Post-test loop implementation, then 6, then Loop finished. Change the 6 to 1 and it prints the numbers 1 to 5, exactly as the pre-test loop does.

loading editor...

regfile

N clearZ clearC clearV clear

x0–x30 are the integer registers.

X0arg00x0000000000000000
X1arg10x0000000000000000
X2arg20x0000000000000000
X3arg30x0000000000000000
X4arg40x0000000000000000
X5arg50x0000000000000000
X6arg60x0000000000000000
X7arg70x0000000000000000
X8ind0x0000000000000000
X90x0000000000000000
X100x0000000000000000
X110x0000000000000000
X120x0000000000000000
X130x0000000000000000
X140x0000000000000000
X150x0000000000000000
X16ip00x0000000000000000
X17ip10x0000000000000000
X18pr0x0000000000000000
X190x0000000000000000
X200x0000000000000000
X210x0000000000000000
X220x0000000000000000
X230x0000000000000000
X240x0000000000000000
X250x0000000000000000
X260x0000000000000000
X270x0000000000000000
X280x0000000000000000
X29fp0x0000000000000000
X30lr0x0000000000000000
SP0x0000000080000000
PC0x0000000000400000
console

Output prints here as your program runs.

Press step or run under the editor, or feed stdin from the box below.

not assembled

example 1try it: run it, or step one instruction at a timeOpen in playground

note

main changes w19 without saving it first, a shortcut these lessons take only in main. A subroutine you write yourself must save any of x19 to x28 it changes and restore them before it returns, as Subroutines: saved registers, pointers, and big arguments shows.

Side by side

The program below runs both kinds of loop from the same start value, one after the other, and labels each line of output. Both loops count up while the counter is at most LIMIT. With START set to 6, the pre-test loop's line stays empty and the post-test loop's line shows its one extra pass:

pre-test:post-test: 6

Set START to 1 and both lines read 1 2 3 4 5. Set it to 5 and both read 5. Only a start past the limit tells the two loops apart.

loading editor...

regfile

N clearZ clearC clearV clear

x0–x30 are the integer registers.

X0arg00x0000000000000000
X1arg10x0000000000000000
X2arg20x0000000000000000
X3arg30x0000000000000000
X4arg40x0000000000000000
X5arg50x0000000000000000
X6arg60x0000000000000000
X7arg70x0000000000000000
X8ind0x0000000000000000
X90x0000000000000000
X100x0000000000000000
X110x0000000000000000
X120x0000000000000000
X130x0000000000000000
X140x0000000000000000
X150x0000000000000000
X16ip00x0000000000000000
X17ip10x0000000000000000
X18pr0x0000000000000000
X190x0000000000000000
X200x0000000000000000
X210x0000000000000000
X220x0000000000000000
X230x0000000000000000
X240x0000000000000000
X250x0000000000000000
X260x0000000000000000
X270x0000000000000000
X280x0000000000000000
X29fp0x0000000000000000
X30lr0x0000000000000000
SP0x0000000080000000
PC0x0000000000400000
console

Output prints here as your program runs.

Press step or run under the editor, or feed stdin from the box below.

not assembled

example 2try it: run it, or step one instruction at a timeOpen in playground

pitfall

Pick a post-test loop only when the body must run at least once, such as showing a menu before the first choice is read. For counting, a pre-test loop is the safer choice: a do-while loop that should have run zero times still runs once, and that extra pass is easy to miss.

Check yourself

  1. A program must show a menu once and keep showing it while the user picks 9. Which kind of loop fits?
  2. In the side-by-side program, what does each line print when START is 4?
  3. What one change turns the program in Pre-test loops into the post-test loop on this page, apart from the start value?
  4. A do-while loop starts i at 10 and repeats while i < 3. How many times does its body run?

answers

show answers
  1. A post-test loop, because the menu must appear at least once.
  2. pre-test: 4 5 and post-test: 4 5.
  3. Deleting b loopcond, the jump to the test before the first pass.
  4. Once: the body runs, and then the first check, i < 3, fails.

Practice