为什么使用JLE代替JL?

时间:2013-10-24 17:45:39

标签: c assembly compiler-construction

我写了以下程序:

#include <stdio.h>

int main()
{
    int i = 0;
    for (; i < 4; i++)
    {
        printf("%i",i);
    }

    return 0;
} 

我使用gcc test.c -o test.o对其进行了编译,然后使用objdump -d -Mintel test.o对其进行了反汇编。我得到的汇编代码(至少是相关部分)如下:

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 < 4,但汇编代码是(在反汇编之后)i <= 3。为什么会这样?为什么会使用JLE代替JL

2 个答案:

答案 0 :(得分:6)

向上计数且持续限制的循环非常常见。编译器有两个选项来实现循环终止检查 - JLEJL。虽然这两种方式看起来完全相同,但请考虑以下几点。

正如您在反汇编列表中所看到的,常量(在您的情况下为3)以1个字节编码。如果你的循环计数为256而不是4,那么对CMP指令使用这种有效的编码将是impossible,并且编译器必须使用“更大”的编码。所以JLE提供了代码密度的微小改进(由于缓存,最终有利于提高性能)。

答案 1 :(得分:1)

它会JLE因为它将值移动了一个。

if (x < 4) {
  // ran when x is 3, 2, 1, 0, -1, ... MIN_INT.
}

在逻辑上等同于

if (x <= 3) {
  // ran when x is 3, 2, 1, 0, -1, ... MIN_INT.
}

为什么编译器选择一个内部表示而不是另一个内部表示通常是优化的问题,但实际上很难知道优化是否是真正的驱动因素。在任何情况下,像这样的功能等同物是反向映射不是100%准确的原因。有很多方法可以编写对相同输入具有相同效果的条件。