【问题标题】:Questions regarding disassembly output关于反汇编输出的问题
【发布时间】:2015-09-21 09:37:05
【问题描述】:

考虑以下 C++ 代码:

bool foo(int x, int y, int z, int P) {
  return (x < P && y > P && z > P);     
}

int tryout(int iS, int iP) {
  if(foo(iS,iS,iS,iP)) {
       printf("%d", 1000);
  } else {
        printf("%d", 10);
  }
  return 0;
}

使用 Intel 的 C++ 编译器时,代码会生成以下汇编代码:

L__routine_start__Z3fooiiii_0:
foo(int, int, int, int):
        cmpl      %ecx, %edi                                    #5.15
        jge       ..B1.4        # Prob 50%                      #5.15
        cmpl      %ecx, %esi                                    #5.24
        jle       ..B1.4        # Prob 50%                      #5.24
        movl      $1, %esi                                      #5.15
        xorl      %eax, %eax                                    #5.15
        cmpl      %ecx, %edx                                    #5.15
        cmovg     %esi, %eax                                    #5.15
        ret                                                     #5.15
..B1.4:                         # Preds ..B1.2 ..B1.1
        xorl      %eax, %eax                                    #5.15
        ret                                                     #5.15
L__routine_start__Z6tryoutii_1:
tryout(int, int):
        pushq     %rsi                                          #11.28
        cmpl      %esi, %edi                                    #5.15
        jge       ..B2.4        # Prob 50%                      #5.15
        jle       ..B2.4        # Prob 50%                      #5.24
        movl      $.L_2__STRING.0, %edi                         #14.8
        movl      $1000, %esi                                   #14.8
        xorl      %eax, %eax                                    #14.8
        call      printf                                        #14.8
        jmp       ..B2.5        # Prob 100%                     #14.8
..B2.4:                         # Preds ..B2.1 ..B2.2
        movl      $.L_2__STRING.0, %edi                         #16.5
        movl      $10, %esi                                     #16.5
        xorl      %eax, %eax                                    #16.5
        call      printf                                        #16.5
..B2.5:                         # Preds ..B2.4 ..B2.3
        xorl      %eax, %eax                                    #19.10
        popq      %rcx                                          #19.10
        ret                                                     #19.10
.L_2__STRING.0:
    .byte   37
    .byte   100
    .byte   0

我有两个问题:

  1. 为什么在tryout 方法中有两次跳转到..B2.4 而只有一个if 语句?
  2. foo 方法中的这段代码有什么作用?

    movl      $1, %esi                                      #5.15
    xorl      %eax, %eax                                    #5.15
    cmpl      %ecx, %edx                                    #5.15
    cmovg     %esi, %eax                                    #5.15
    

【问题讨论】:

  • 这应该是C 还是 C++?
  • 您对foo() 的调用将始终返回false,因此您将始终打印10。我想知道编译器是否最初允许这两种情况,然后在优化期间添加了第二个跳转语句调用 printf 10 100% 的时间,因为打印 1000 永远不会发生。
  • @cad: 代码用c++编写,用-O3编译
  • 你的表情减少到iS &lt; iP &amp;&amp; iS &gt; iP &amp;&amp; iS &gt; iP。由于iS &lt; iP 不能为真而iS &gt; iP,结果为0/假。由于这将始终为 0/false,因此无论结果如何,都将始终跳转到 ..B2.4 以打印 10。事实上,cmpljgejle 指令甚至不应该存在,因为它是不必要的;它们应替换为jmp ..B2.4。不知道为什么没有发生这种情况......
  • @ChronoKitsune 实际上从cmpl %esi, %edijmp ..B2.5 的所有内容都可以删除——无需跳转。该程序实际上是对 printf 的一次调用

标签: c++ optimization assembly x86


【解决方案1】:
x < P && y > P && z > P

有 3 个条件。把它放在一个if 中并不意味着它只需要一个jcc 来评估。 (注意foo() 被内联了。没有call 指令。)

如果编译器不能将多个测试组合到一个表达式中,那么编译器通常会按顺序对每个条件进行测试和分支。在这种情况下,icc 只是内联了它对foo() 所做的事情,但它可以省略第二个cmp,因为它正在测试相同的两个变量。注意两个分支的分支目标是如何相同的,因为它们都在检查条件的不同部分以获取相同的if。你没有得到第三个jcc,因为第二个和第三个条件是相同的,所以编译器将它们组合起来。

正如 cmets 所指出的,ICC 应该注意到这种情况根本不需要任何测试。一个或另一个分支将总是被占用,因此它可以完成what gcc and clang do,并将其编译为对 printf 的简单调用,无需检查任何内容。

【讨论】:

  • 感谢您的解释。我也发现它是内联方法 foo()。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多