为什么比特操作和乘法在这里优先于一个条件?
我在源代码中发现了这一点操作:
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?