【问题标题】:why less than expression converts into less than or equal to expression in gcc为什么小于表达式转换为小于或等于 gcc 中的表达式
【发布时间】:2012-07-11 02:18:00
【问题描述】:

我正在研究代码优化并了解 gcc 内部结构。我在我的程序中写了一个简单的表达式,我检查了那个表达式的 gimple 表示,我不知道为什么 gcc 会这样做。 说我有一个表情:

if(i < 9)

然后在 gimple 表示中它将被转换为

if(i <= 8)

我不知道为什么 gcc 会这样做。是不是某种优化,如果是的话,谁能告诉我它如何优化我们的程序?

【问题讨论】:

  • 我无法回答为什么特别是 gcc 会这样做,但我知道如果我正在编写编译器,我为什么会这样做。在源代码到源代码的转换阶段将
  • Cyber​​ 编译器(在 Pascal 和 Fortran 中)也进行了这种优化。我认为他们更关心常量8 而不是9,因为8 是通过移动1(一个始终保存在寄存器中的常量)产生的,而9 需要几个操作来生成或(喘气)从内存常量中获取。我想知道:它对if (i &lt; 8) 做同样的事情吗?
  • 是的,它会对像这样的任何表达式做同样的事情

标签: c optimization gcc gimple


【解决方案1】:

规范化有助于检测 CommonSubExpressions,例如:

#include <stdio.h>

int main(void)
{
unsigned u, pos;
char buff[40];

for (u=pos=0; u < 10; u++) {
        buff[pos++] = (u <5) ? 'A' + u : 'a' + u;
        buff[pos++] = (u <=4) ? '0' + u : 'A' + u;
        }
buff[pos++] = 0;
printf("=%s=\n", buff);
return 0;
}

GCC -O1 会将其编译成:

         ...
        movl    $1, %edx
        movl    $65, %ecx
.L4:
        cmpl    $4, %eax
        ja      .L2
        movb    %cl, (%rsi)
        leal    48(%rax), %r8d
        jmp     .L3
.L2:
        leal    97(%rax), %edi
        movb    %dil, (%rsi)
        movl    %ecx, %r8d
.L3:
        mov     %edx, %edi
        movb    %r8b, (%rsp,%rdi)
        addl    $1, %eax
        addl    $1, %ecx
        addl    $2, %edx
        addq    $2, %rsi
        cmpl    $10, %eax
        jne     .L4
        movb    $0, 20(%rsp)
        movq    %rsp, %rdx
        movl    $.LC0, %esi
        movl    $1, %edi
        movl    $0, %eax
        call    __printf_chk
         ...

GCC -O2 实际上会删除整个循环并用一个赋值流替换它。

【讨论】:

    【解决方案2】:

    考虑以下 C 代码:

    int i = 10;
    
    if(i < 9) {
      puts("1234");
    }
    

    还有等效的 C 代码:

    int i = 10;
    
    if(i <= 8) {
      puts("asdf");
    }
    

    在没有优化的情况下,两者都生成完全相同的组装顺序:

    40052c:       c7 45 fc 0a 00 00 00    movl   $0xa,-0x4(%rbp)
    400533:       83 7d fc 08             cmpl   $0x8,-0x4(%rbp)
    400537:       7f 0a                   jg     400543 <main+0x1f>
    400539:       bf 3c 06 40 00          mov    $0x40063c,%edi
    40053e:       e8 d5 fe ff ff          callq  400418 <puts@plt>
    400543:       .. .. .. ..             .. ..  ..
    

    由于我不熟悉 GCC 的实现,我只能推测为什么要进行转换。也许它使代码生成器的工作更容易,因为它只需要处理一个案例。我希望有人能提出更明确的答案。

    【讨论】:

    • 就个人而言,我认为编译器总是以这样的方式规范化代码很有用。否则,您会遇到这样的情况:两段看似等效的代码导致 asm 略有不同(可能具有不同的性能),而认为自己比实际聪明得多的程序员会比较 asm 并编写形式生成“更好” asm 的代码,而不是更能代表其意图的代码形式。这会导致可怕的混淆代码。
    • 这个答案信息太少,文字太多
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-10
    • 1970-01-01
    • 2012-03-21
    • 1970-01-01
    • 1970-01-01
    • 2017-08-28
    相关资源
    最近更新 更多