8.4.2. Loops in assembly

Like if-statements, loops in assembly are also implemented using jump instructions. However, loops enable instructions to be revisited based on the result of an evaluated condition.

The sumUp() function shown below sums up all the positive integers from 1 to a user-defined integer. This code is intentionally written sub-optimally to illustrate a while-loop in C:

int sumUp(int n) {
    //initialize total and i
    int total = 0;
    int i = 1;

    while (i <= n) {  //while i is less than or equal to n
        total += i;   //add i to total
        i+=1;          //increment i by 1
    }
    return total;
}

Compiling this code with the -m32 option and disassembling it using GDB yields the following assembly code:

(gdb) disas sumUp
Dump of assembler code for function sumUp:
  0x0804840b <+0>:   push   %ebp
  0x0804840c <+1>:   mov    %esp,%ebp
  0x0804840e <+3>:   sub    $0x10,%esp
  0x08048411 <+6>:   movl   $0x0,-0x8(%ebp)
  0x08048418 <+13>:  movl   $0x1,-0x4(%ebp)
  0x0804841f <+20>:  jmp    0x804842b <sumUp+32>
  0x08048421 <+22>:  mov    -0x4(%ebp),%eax
  0x08048424 <+25>:  add    %eax,-0x8(%ebp)
  0x08048427 <+28>:  add   $0x1,-0x4(%ebp)
  0x0804842b <+32>:  mov    -0x4(%ebp),%eax
  0x0804842e <+35>:  cmp    0x8(%ebp),%eax
  0x08048431 <+38>:  jle    0x8048421 <sumUp+22>
  0x08048433 <+40>:  mov    -0x8(%ebp),%eax
  0x08048436 <+43>:  leave
  0x08048437 <+44>:  ret

Again, we will not draw out the stack explicitly in this example. However, we encourage readers to draw the stack out themselves. Let’s analyze this assembly segment in parts:

The first five instructions

The first five instructions of this function set the stack up for function execution:

0x0804840b <+0>:   push   %ebp                 # save ebp on stack
0x0804840c <+1>:   mov    %esp,%ebp            # update ebp (new stack frame)
0x0804840e <+3>:   sub    $0x10,%esp           # add 16 bytes to stack frame
0x08048411 <+6>:   movl   $0x0,-0x8(%ebp)      # place 0 at ebp-0x8 (total)
0x08048418 <+13>:  movl   $0x1,-0x4(%ebp)      # place 1 at ebp-0x4 (i)

Recall that stack locations store temporary variables in a function. For simplicity we will refer to the location marked by %ebp - 0x8 as total and %ebp - 0x4 as i. The input parameter to sumUp() is located at %ebp + 0x8.

The heart of the loop

The next seven instructions in the sumUp() function represent the heart of the loop:

0x0804841f <+20>:  jmp    0x804842b <sumUp+32>  # goto <sumUp+32>
0x08048421 <+22>:  mov    -0x4(%ebp),%eax       # copy i to eax
0x08048424 <+25>:  add    %eax,-0x8(%ebp)       # add i to total (total+=i)
0x08048427 <+28>:  add    $0x1,-0x4(%ebp)       # add 1 to i (i+=1)
0x0804842b <+32>:  mov    -0x4(%ebp),%eax       # copy i to eax
0x0804842e <+35>:  cmp    0x8(%ebp),%eax        # compare i with n
0x08048431 <+38>:  jle    0x8048421 <sumUp+22>  # if (i <= n) goto <sumUp+22>

The first instruction is a direct jump to <sumUp+32>, which sets the instruction pointer (%eip) to address 0x804842b.

The next instructions that execute (<sumUp+32> and <sumUp+35>) copy the value of i to register %eax and compares i with the first parameter to the sumUp() function (or n). The cmp instruction sets the appropriate condition codes in preparation for the jle instruction at <sumUp+38>.

The jle instruction at <sumUp+38> then executes. If i is less than or equal to n, the branch is taken and program execution jumps to <sumUp+22> and %eip is set to 0x8048421. The follow instructions then execute in sequence:

  • mov -0x4(%ebp),%eax copies i to register %eax.

  • add %eax,-0x8(%ebp) adds i to total (i.e. total+=i)

  • add $0x1,-0x4(%ebp) increments i by 1 (i.e. i+=1)

  • mov -0x4(%ebp),%eax copies i to register %eax

  • cmp 0x8(%ebp),%eax compares i to n

  • jle 0x8048421 <sumUp+22> jumps back to the beginning of this instruction sequence if i is less than or equal to n.

If the branch is not taken at <sumUp+38> (i.e. i is not less than or equal to n), total is placed in the return register, and the function exits.

Table 1 shows the assembly and C goto form of the sumUp() function side by side:

Table 1. Translating sumUp() into goto C form.
Assembly Translated Goto Form
<sumUp>:
<+0>:   push   %ebp
<+1>:   mov    %esp,%ebp
<+3>:   sub    $0x10,%esp
<+6>:   movl   $0x0,-0x8(%ebp)
<+13>:  movl   $0x1,-0x4(%ebp)
<+20>:  jmp    <sumUp+32>
<+22>:  mov    -0x4(%ebp),%eax
<+25>:  add    %eax,-0x8(%ebp)
<+28>:  addl   $0x1,-0x4(%ebp)
<+32>:  mov    -0x4(%ebp),%eax
<+35>:  cmp    0x8(%ebp),%eax
<+38>:  jle    <sumUp+22>
<+40>:  mov    -0x8(%ebp),%eax
<+43>:  leave
<+44>:  ret
int sumUp(int n) {
    int total = 0;
    int i = 1;
    goto start;
body:
    total += i;
    i += 1;
start:
    if (i <= n) {
        goto body;
    }
    return total;
}

This code is also equivalent to the following C code without goto statements:

int sumUp(int n) {
    int total = 0;
    int i = 1;
    while (i <= n) {
        total += i;
        i += 1;
    }
    return total;
}

For loops in Assembly

The primary loop in the sumUp() function can also be written as a for loop:

int sumUp2(int n) {
    int total = 0;             //initialize total to 0
    int i;
    for (i = 1; i <= n; i++) { //loop initialize i to 1, increment i by 1 while i<=n
        total += i;            //updates total by i
    }
    return total;
}

It yields identical assembly code as our while loop example. We repeat the assembly code below and annotate each line with its English translation:

0x08048438 <+0>:   push   %ebp                    #save ebp
0x08048439 <+1>:   mov    %esp,%ebp               #update ebp (new stack frame)
0x0804843b <+3>:   sub    $0x10,%esp              #add 16 bytes to stack frame
0x0804843e <+6>:   movl   $0x0,-0x8(%ebp)         #place 0 at ebp-0x8 (total)
0x08048445 <+13>:  movl   $0x1,-0x4(%ebp)         #place 1 at ebp-0x4 (i)
0x0804844c <+20>:  jmp    0x8048458 <sumUp2+32>   #goto <sumUp2+32>
0x0804844e <+22>:  mov    -0x4(%ebp),%eax         #copy i to %eax
0x08048451 <+25>:  add    %eax,-0x8(%ebp)         #add %eax to total (total+=i)
0x08048454 <+28>:  addl   $0x1,-0x4(%ebp)         #add 1 to i (i+=1)
0x08048458 <+32>:  mov    -0x4(%ebp),%eax         #copy i to %eax
0x0804845b <+35>:  cmp    0x8(%ebp),%eax          #compare i with n
0x0804845e <+38>:  jle    0x804844e <sumUp2+22>   #if (i <= n) goto <sumUp2+22>
0x08048460 <+40>:  mov    -0x8(%ebp),%eax         #copy total to %eax
0x08048463 <+43>:  leave                          #prepare to leave the function
0x08048464 <+44>:  ret                            #return total

To understand why the for loop version of this code results in identical assembly to the while loop version of the code, recall that the for loop has the following representation:

for ( <initialization>; <boolean expression>; <step> ){
    <body>
}

And that it is equivalent to the following while loop representation:

<initialization>
while (<boolean expression>) {
    <body>
    <step>
}

Since every for loop can be represented by a while loop, the following two C programs are equivalent representations for the above assembly:

Table 2. Equivalent ways to write the sumUp function.
For loop While loop
int sumUp2(int n) {
    int total = 0;
    int i = 1;
    for (i; i <= n; i++) {
        total += i;
    }
    return total;
}
int sumUp(int n){
    int total = 0;
    int i = 1;
    while (i <= n) {
        total += i;
        i += 1;
    }
    return total;
}