目录 Table of Contents
循环的汇编语言表示
如何实现循环 (Loops)
所有的循环模式 (while, do-while, for) 都转换为 "do-while" 形式, 再转换成汇编
历史上采用过多种转换模式, 经历了 "否定之否定" 的过程, 它现在采用 do-while 模式, 前几年采用 jump-to=middile 模式, 再往前更早还是采用这种 do-while 方式.
do-while 循环
int fact_do (int x)
{
int result = 1;
do{
result *= x;
x = x - 1;
}while (x > 1);
return result;
}
这么一段代码, 编译器先把它转换为 goto 模式如下
int fact_goto(int x)
{
int result = 1;
loop :
result *= x;
x = x - 1;
if (x > 1)
goto loop;
return result;
}
出来的汇编代码如下 :
fact_goto :
pushl %ebp ; Set up
movl %esp, %ebp ; Setup
movl $1, %eax ; eax = 1
movl 8(%ebp), %edx ; edx = x
L11 :
imull %edx, %eax ; result *= x
decl %edx ; x--
cmpl $1, %edx ; Compare x : 1
jg L11 ; if > goto loop
movl %ebp. %esp
popl %ebp
ret ; Finish
其中 edx 用于存储 x, eax 用于存储返回值
while 循环 (版本1)
int fact_while(int x)
{
int result = 1;
while(x > 1){
result *= x;
x = x - 1;
}
return result;
}
这个实例与上一个等价, 因为它也是做完这么一个阶乘.
goto version
int fact_while_goto(int x)
{
int result = 1;
loop:
if(!(x > 1))
goto done;
result *= x;
x = x - 1;
goto loop;
done:
return result;
}
while 循环 (版本2, do-while 模式)
int fact_while(int x)
{
int result = 1;
while(x > 1){
result *= x;
x = x - 1;
}
return result;
}
goto version :
int fact_while_goto2(int x)
{
int result = 1;
if(!(x > 1))
goto done;
loop :
result *= x;
x = x - 1;
if(x > 1)
goto loop;
done :
return result;
}
目前 gcc 就是用这样的模式
还是 do while, 在入口处判断, 出口再做一次判断, 就是不是跳回去.
它这个内部循环实际上和 do-while 是一样的, 然后在这个入口点地方, 再做一个额外的条件测试.
for 循环
首先是说, 先设置一个初始值
int result;
for(result = 1;p != 0; p = p >> 1)
{
if(p & 0x1)
result *= x;
x = x * x;
}
result 这边等于 1, 然后进行一个 test p != 0, 再进行循环 body, 然后进行 update p = p >> 1.
编译器先把 for 变形为一个 while 循环, 然后 while 循环变形成 do-while 模式, 最终成为一个 goto 版本.
补充
历史上 gcc 采用过多种转换模式, 经历了 "否定之否定" 的过程
while 循环(版本3, jump-to-middle)
这是之前 gcc 采用的一种模式
原始的 C 代码还是 while 循环 :
int fact_while(int x)
{
int result = 1;
while(x > 1){
result *= x;
x = x - 1;
}
return result;
}
它把这个代码转换为下面这个模式, 这个模式看起来有一点点奇怪
:
int fact_while_goto3(int x)
{
int result = 1;
goto middle;
loop :
result *= x;
x = x - 1;
middle :
if (x > 1)
goto loop;
return result;
}
jump-to-middle 实例
我们用 gcc 3.4.4 加上 -O2 来编译这个代码, 生成如下代码 :
; x in %edx, result in %eax
jmp L34 ; goto middle
L35: ; Loop :
imull %edx, %eax ; result *= x
decl %edx ; x--
L34: ; Middle
cmpl $1, %edx ; x : 1
jg L35 ; if >, goto Loop
这个看起来有点意思, 一进来先来个 jmp, 跳到判断的地方, 然后再做循环.
当年这么做是有它的道理的, 避免了双重测试, 就是无条件跳转指令的开销运行是非常低的. 不过这个还是没有说到重要的地方上去, 具体的道理见下一节微体系结构背景.