I wrote the following program:
#include <stdio.h>
int main()
{
int i = 0;
for (; i < 4; i++)
{
printf("%i",i);
}
return 0;
}
I compiled it using gcc test.c -o test.o, then disassembled it using objdump -d -Mintel test.o. The assembly code I got (at least the relevant part) is the following:
0804840c <main>:
804840c: 55 push ebp
804840d: 89 e5 mov ebp,esp
804840f: 83 e4 f0 and esp,0xfffffff0
8048412: 83 ec 20 sub esp,0x20
8048415: c7 44 24 1c 00 00 00 mov DWORD PTR [esp+0x1c],0x0
804841c: 00
804841d: eb 19 jmp 8048438 <main+0x2c>
804841f: 8b 44 24 1c mov eax,DWORD PTR [esp+0x1c]
8048423: 89 44 24 04 mov DWORD PTR [esp+0x4],eax
8048427: c7 04 24 e8 84 04 08 mov DWORD PTR [esp],0x80484e8
804842e: e8 bd fe ff ff call 80482f0 <printf@plt>
8048433: 83 44 24 1c 01 add DWORD PTR [esp+0x1c],0x1
8048438: 83 7c 24 1c 03 cmp DWORD PTR [esp+0x1c],0x3
804843d: 7e e0 jle 804841f <main+0x13>
804843f: b8 00 00 00 00 mov eax,0x0
8048444: c9 leave
8048445: c3 ret
I noticed that, although my compare operation was i < 4, the assembly code is (after disassembly) i <= 3. Why does that happen? Why would it use JLE instead of JL?
Loops that count upwards, and have constant limit, are very common. The compiler has two options to implement the check for loop termination - JLE and JL. While the two ways seem absolutely equivalent, consider the following.
As you can see in the disassembly listing, the constant (3 in your case) is encoded in 1 byte. If your loop counted to 256 instead of 4, it would be impossible to use such an efficient encoding for the CMP instruction, and the compiler would have to use a "larger" encoding. So JLE provides a marginal improvement in code density (which is ultimately good for performance because of caching).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With