Bhu*_*bey 3 c c++ x86 assembly micro-optimization
单个x86指令可以在"0"和"1"之间切换布尔值吗?
我想到了以下方法,但都导致了两个指令与-cc标志的gcc.
status =! status;
status = 1 - status;
status = status == 0 ? 1: 0;
int flip[2] = {1, 0};
status = flip[status];
Run Code Online (Sandbox Code Playgroud)
有更快的方法吗?
这就是我尝试过的:https://godbolt.org/g/A3qNUw
我需要的是一个切换输入和返回的函数,以编译为一条指令的方式编写.与此功能类似的东西:
int addOne(int n) { return n+1; }
Run Code Online (Sandbox Code Playgroud)
lea eax, [rdi+1] # return n+1 in a single instruction
ret
Run Code Online (Sandbox Code Playgroud)
要翻转整数,请使用xor如下: foo ^= 1.
gcc已经知道了这个优化bool,所以你可以return !status;像普通人一样不失效率.gcc status ^= 1也会编译为xor指令.实际上,除了表查找之外,您的所有想法都会编译为xor具有bool输入/返回值的单个指令.
检查出来的Godbolt编译探险用gcc -O3,与ASM输出窗格的bool和int.
MYTYPE func4(MYTYPE status) {
status ^=1;
return status;
}
# same code for bool or int
mov eax, edi
xor eax, 1
ret
Run Code Online (Sandbox Code Playgroud)
与
MYTYPE func1(MYTYPE status) {
status = !status;
return status;
}
# with -DMYTYPE=bool
mov eax, edi
xor eax, 1
ret
# with int
xor eax, eax
test edi, edi
sete al
ret
Run Code Online (Sandbox Code Playgroud)
bool不同int?x86-64 System V ABI要求调用者传递一个bool0或1的值,而不是任何非零整数.因此,编译器可以假设关于输入.
但是int foo,C表达式!foo需要"布尔化"该值. !foo具有类型_Bool/(又名bool如果你#include <stdbool.h>),并转换该回的整数必须产生0或1的值.如果编译器不知道foo必须是0或者1,它不能优化!foo到foo^=1,并且可以没有意识到foo ^= 1在truthy/falsy之间翻转一个值.(在C语言中if(foo)意味着if(foo != 0)).
这就是为什么你得到test/setcc(int通过在之前调零xor寄存器将零扩展为32位test)的原因.
相关: 编译器中的布尔值为8位.对他们的操作是否效率低下?.类似的东西(bool1 && bool2) ? x : y并不总是像你希望的那样有效地编译.编译器非常好,但确实有错过优化错误.
mov指令怎么样?如果编译器不需要/想要保留旧的未翻转值以供以后使用,它将在内联时消失.但是在一个独立的函数中,第一个arg处于edi,并且返回值需要在eax(在x86-64 System V调用约定中).
像这样的微小函数非常接近你可能得到的大函数的一部分(如果这个翻转不能被优化成其他东西),但是需要将结果放在不同的寄存器中是一个混淆因素.
x86没有copy-and-xor整数指令,因此对于独立函数,至少mov需要从arg传递寄存器复制到eax.
lea很特别:它是为数不多的整数ALU指令之一,它可以将结果写入不同的寄存器而不是破坏其输入. lea是一个复制和移位/添加指令,但在x86中没有copy-and-xor指令.许多RISC指令集具有3操作数指令,例如MIPS可以执行xor $t1, $t2, $t3.
AVX引入了矢量指令的非破坏性版本(在很多代码中保存了很多movdqa/ movups寄存器复制),但是对于整数,只有少数新指令可以执行不同的操作. rorx eax, ecx, 16 例如eax = rotate_right(ecx, 16),并使用非破坏性AVX指令使用的相同VEX编码.
| 归档时间: |
|
| 查看次数: |
764 次 |
| 最近记录: |