答案 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中的x86和其他链接。
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架构也是如此。