为什么比特操作和乘法在这里优先于一个条件?
我在源代码中发现了这一点操作:
A = 0b0001;
B = 0b0010;
C = 0b0100;
flags |= !!(flags & (A | B)) * C;
我看不出,为什么使用这个复杂的表达式。 flags & (A | B) flags过滤到A | B A | B 现在,如果flags设置为任何true ,则将其转换为true ,否则为false 。 true * C == C和false * C == 0 。 仅仅使用flags = flags ? flags | C会慢flags = flags ? flags | C flags = flags ? flags | C flags = flags ? flags | C ?
在GCC 7.3编译
bitwise:
mov eax, edi
mov edx, edi
or edx, 4
test al, 3
cmovne eax, edx
ret
condition:
mov eax, edi
mov edx, 0
or eax, 4
test edi, edi
cmove eax, edx
ret
Clang 6.0还消除了多余的呼叫:
bitwise:
xor eax, eax
test dil, 3
setne al
shl eax, 2
or eax, edi
ret
condition:
mov eax, edi
or eax, 4
test edi, edi
cmove eax, edi
ret
我要监督一些事情吗?
您在您的版本中遗漏了A|B的测试(由MichaelPetch发现)。 而且,如果测试失败,则您正在清零flags而不是将其保留为未修改状态。 请记住,如果存在A或B中的任何A ,则需要通过设置必需/隐含的标志C来影响其他位中的其他标志。 (请参阅您链接的源代码中的注释: Add TRANSFER_BIT if missing (implied) )
而等价的三元运算符只是替换布尔乘法hack会是flags |= (flags & (A|B)) ? C : 0 flags |= (flags & (A|B)) ? C : 0 。
另一种方法是flags = (flags & (A|B)) ? flags|C : flags flags = (flags & (A|B)) ? flags|C : flags ,编译得更好。
我在Godbolt上修复了你的代码以使用正确的表达式,是的,它编译为gcc / clang和MSVC的更好看的asm。 可能这在更大的功能的情况下仍然是正确的,这种更大的功能会带来更多带有flags东西。
看起来这个版本用gcc / clang / MSVC编译最好:
int condition(int flags) {
flags = (flags&(A|B)) ? flags | C : flags;
return flags;
}
; gcc/clang both emit
mov eax, edi
or eax, 4
test dil, 3
cmove eax, edi
ret
和MSVC是类似的,但与一个and而不是test ,和一个额外的mov由于破坏性and 。
铿锵6.0也消除了多余的通话
什么call ? 这段代码中没有函数调用。 你是说clang避免了gcc使用的冗余mov edx, edi ? 是的,海湾合作委员会是愚蠢的。
为什么有人会像他们那样写它?
也许他们过去的经验是使用旧版编译器,而这些编译器在三进制运算符方面做得不好,或者在没有cmov非x86平台上cmov并且实际上在源代码中使用*运算符是获得无分支asm的唯一方法之一。
我的猜测是,不管谁写了这个技巧,而且还用它来代替三元操作符。 这不是一个好主意,也不会导致更好的代码。
链接地址: http://www.djcxy.com/p/15071.html上一篇: Why bitoperation and multiplication is prefered here over a condition?
