这更多的是为了演示编译器是什么有能力,而不是每个编译器都会做的事情。来源:
#include <stdio.h>
int main(void)
{
int i, sum = 0;
for(i=0; i<5; i++) {
sum+=i;
}
printf("%d\n", sum);
return 0;
}
请注意printf
我已经添加了。如果不使用该变量,编译器将优化整个循环。
使用 -O0 编译(无优化)
gcc -Wall -O0 -S -c lala.c
:
.L3:
movl -8(%rbp), %eax
addl %eax, -4(%rbp)
addl $1, -8(%rbp)
.L2:
cmpl $4, -8(%rbp)
jle .L3
循环以“愚蠢”的方式发生,-8(%rbp)
是变量i
.
使用 -O1 编译(优化级别 1)
gcc -Wall -O1 -S -c lala.c
:
movl $10, %edx
循环已被完全删除并替换为等效值。
在展开过程中,编译器会查看会发生多少次迭代,并尝试通过执行更少的迭代来展开。例如,循环体可能会重复两次,这将导致分支数量减半。 C 中存在这样的情况:
int i = 0, sum = 0;
sum += i;
i++;
for(; i<5;i++) {
sum+=i;
i++;
sum+=i;
}
请注意,必须从循环中提取一次迭代。因为5是奇数,所以不能简单地通过复制内容来减半工作量。在这种情况下,循环只会进入两次。产生的汇编代码-O0
:
movl -8(%rbp), %eax
addl %eax, -4(%rbp)
addl $1, -8(%rbp)
jmp .L2
.L3:
movl -8(%rbp), %eax
addl %eax, -4(%rbp)
addl $1, -8(%rbp)
movl -8(%rbp), %eax
addl %eax, -4(%rbp)
addl $1, -8(%rbp)
.L2:
cmpl $4, -8(%rbp)
在 C 中完全展开:
for(i=0; i<5;i++) {
sum+=i;
i++;
sum+=i;
i++;
sum+=i;
i++;
sum+=i;
i++;
sum+=i;
}
这次实际上只进入了一次循环。生产的组件-O0
:
.L3:
movl -8(%rbp), %eax
addl %eax, -4(%rbp)
addl $1, -8(%rbp)
movl -8(%rbp), %eax
addl %eax, -4(%rbp)
addl $1, -8(%rbp)
movl -8(%rbp), %eax
addl %eax, -4(%rbp)
addl $1, -8(%rbp)
movl -8(%rbp), %eax
addl %eax, -4(%rbp)
addl $1, -8(%rbp)
movl -8(%rbp), %eax
addl %eax, -4(%rbp)
addl $1, -8(%rbp)
.L2:
cmpl $4, -8(%rbp)
jle .L3