2_循环的汇编语言表示

循环的汇编语言表示

如何实现循环 (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, 跳到判断的地方, 然后再做循环.

当年这么做是有它的道理的, 避免了双重测试, 就是无条件跳转指令的开销运行是非常低的. 不过这个还是没有说到重要的地方上去, 具体的道理见下一节微体系结构背景.