编译器什么时候会在C / C ++源代码中优化汇编代码?

时间:2016-12-23 03:27:42

标签: c++ c assembly optimization inline-assembly

大多数编译器不优化内联汇编代码(VS2015gcc),它允许我们编写它不支持的新指令。

但是C / C ++编译器何时应该实现内联汇编优化?

2 个答案:

答案 0 :(得分:6)

通常,编译器不会优化内联汇编的内容。也就是说,他们不会删除或更改装配块中的指令。特别是,gcc只是简单地将内联汇编的主体传递给底层汇编程序(在这种情况下为gas)。

但是,优秀的编译器可以优化围绕你的内联汇编,在某些情况下甚至可能完全省略执行内联汇编代码!例如,如果Gcc确定程序集的声明输出已经死亡,则可以执行此操作。它还可以将装配块从循环中提升或将多个调用合并为一个。所以它永远不会与块内的指令混淆,但改变块执行的次数是完全合理的。当然,如果块具有其他一些重要的副作用,也可以禁用此行为。

扩展asm语法的gcc文档中包含了所有这些内容的good examples

答案 1 :(得分:6)

从不。这将破坏内联汇编的目的,即完全满足您的要求。

如果要以编译器可以理解和优化的方式使用目标CPU指令集的全部功能,则应该使用内部函数,而不是内联asm

e.g。而不是popcnt的内联asm,请使用int count = __builtin_popcount(x);(在使用-mpopcnt编译的GNU C中)。 Inline-asm也是特定于编译器的,所以如果有任何内在函数更具可移植性,特别是如果你使用英特尔的x86内在函数,它可以在所有可以针对x86的主要编译器中得到支持。使用#include <x86intrin.h>,您可以使用int _popcnt32 (int a)可靠地获取popcnt x86指令。请参阅Intel's intrinsics finder/guide标记wiki中的和其他链接。

int count(){ 
  int total = 0;
  for(int i=0 ; i<4 ; ++i)
    total += popc(i);
  return total;
}

通过gcc6.3编译#define popc _popcnt32

    mov     eax, 4
    ret

clang 3.9 with an inline-asm definition of popc, on the Godbolt compiler explorer

    xor     eax, eax
    popcnt  eax, eax
    mov     ecx, 1
    popcnt  ecx, ecx
    add     ecx, eax
    mov     edx, 2
    popcnt  edx, edx
    add     edx, ecx
    mov     eax, 3
    popcnt  eax, eax
    add     eax, edx
    ret

这是内联asm击败常量传播的典型示例,以及为什么如果可以避免它,就不应该将它用于性能:https://gcc.gnu.org/wiki/DontUseInlineAsm

这是我用于此测试的内联asm定义:

int popc_asm(int x) {
  // force use of the same register because popcnt has a false dependency on its output, on Intel hardware
  // this is just a toy example, though, and also demonstrates how non-optimal constraints can lead to worse code
  asm("popcnt %0,%0" : "+r"(x));
  return x;
}

如果您不知道popcnt has a false dependency on its output register on Intel hardware,那么您应该尽可能将其留给编译器。

使用编译器不知道的特殊指令是内联asm的一个用例,但如果编译器不知道它,它肯定无法对其进行优化。在编译器擅长优化内在函数之前(例如对于SIMD指令),内联asm对于这种事情更为常见。但是我们现在已经有很多年了,编译器通常对内在函数很好,即使对于像ARM这样的非x86架构也是如此。