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

c24*_*726 0 c c++ optimization assembly inline-assembly

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

但什么时候C/C++编译器应该实现内联汇编优化?

Bee*_*ope 6

通常,编译器不会优化内联汇编的内容.也就是说,它们不会删除或更改装配块中的指令.特别是,gcc只需将内联汇编的主体不变地传递给底层汇编程序(gas在本例中).

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

关于扩展asm语法的gcc文档有一些很好的例子.


Pet*_*des 6

决不.这将破坏内联汇编的目的,即完全符合您的要求.

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

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


int count(){ 
  int total = 0;
  for(int i=0 ; i<4 ; ++i)
    total += popc(i);
  return total;
}
Run Code Online (Sandbox Code Playgroud)

#define popc _popcnt32gcc6.3 编译:

    mov     eax, 4
    ret
Run Code Online (Sandbox Code Playgroud)

popc在Godbolt编译器资源管理器上使用inline-asm定义的clang 3.9:

    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
Run Code Online (Sandbox Code Playgroud)

这是内联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;
}
Run Code Online (Sandbox Code Playgroud)

如果您不知道popcnt它对英特尔硬件上的输出寄存器具有错误依赖性,那么您应该尽可能将其留给编译器.


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