【问题标题】:gcc loop construct changes to assembly code [duplicate]gcc循环构造更改为汇编代码[重复]
【发布时间】:2011-12-05 13:48:15
【问题描述】:

为什么 gcc 编译器在创建汇编代码时将 while 循环转换为 do-while 结构?我知道任何while循环都可以重写为do-while,例如在c中

while (test) { ... }

可以改写为

if ( !test ) goto skip;
do {
. . .
} while ( test );
skip:

【问题讨论】:

  • 因为在汇编语言中没有for 或while 循环。
  • 如果要讲汇编,贴出编译器生成的汇编代码。

标签: c gcc assembly x86


【解决方案1】:

基于 anatolyg 的回答,您可能想知道 为什么 do-while 构造更有效,特别是考虑到如图所示,测试已重复(因此生成的代码更大)。答案是,测试表达式通​​常可以在循环进入时被证明总是正确的,所以

    if ( !test ) goto skip;
loop:
    . . . // loop body
    if ( test ) goto loop;
skip:
    . . . // continue the program

可以简化为

loop:
    . . . // loop body
    if ( test ) goto loop;
    . . . // continue program

现在,为什么不与原始转换同时进行,如果编译器不能证明循环将至少循环一次,则避免原始转换?因为证明循环将至少循环一次的算法实际上是一个通用的 if 条件优化器。通常的优化顺序是这样的:

  1. 将所有循环转换为“规范”形式(或多或少如上面的第一个代码块所示)
  2. 进行大量优化,期望以规范形式出现循环,例如所示的 if 语句消除。
  3. 在完成所有关心循环的事情后,尝试去规范化可以消除冗余的地方。

要知道的另一件事是,有时会故意留下重复的测试,因为编译器希望这样可以产生更好的运行时行为。例如,如果编译器有理由相信循环通常循环很多次,但不能证明它总是循环至少一次,那么条件分支指令循环上方几乎总是会落空,循环下方的条件分支几乎总是会跳转。在这种情况下,将它们分开可能会使 CPU 的分支预测器更加准确。作为现代乱序 CPU 速度的限制因素,分支预测准确性仅次于缓存友好性。

【讨论】:

  • 每次循环也只有 1 个分支(只是向后的一个)而不是两个。
  • 是的,但是如果编译器确实在循环顶部重新组合了测试表达式,那么您将在底部有一个 unconditional 分支,这在现代几乎是免费的CPU——它占用一个解码单元,但从未进入主管道。
  • 非常感谢您的出色回复。
【解决方案2】:

通常do-while 循环比while 循环更有效。由于编译器想要创建一个快速(而不是可读)的程序,它会将大多数 while 循环转换为 do-while 循环。

实际上,仅使用if 和goto(近似于汇编语言)来实现while 循环可以如下完成:

start:
    if ( !test ) goto skip;
    . . . // loop body
    goto start;
skip:
    ; // continue the program

这与do-while 实现等效但效率可能较低(因为后者在循环内的代码行少了1 行):

    if ( !test ) goto skip;
loop:
    . . . // loop body
    if ( !test ) goto loop;
skip:
    ; // continue the program

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-22
    • 2023-01-31
    • 1970-01-01
    • 1970-01-01
    • 2012-02-12
    • 1970-01-01
    • 2015-04-26
    • 1970-01-01
    相关资源
    最近更新 更多