我在源代码中找到了这个位操作:
A = 0b0001;
B = 0b0010;
C = 0b0100;
flags |= !!(flags & (A | B)) * C;
我无法看到,为什么要使用这种复杂的表达方式。 flags & (A | B)
过滤flags
到A | B
。如果标志设置为任何内容,它现在转换为true
,否则转换为false
。 true * C == C
和false * C == 0
。使用flags = flags ? flags | C
会慢吗?
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 also eliminate the redundant call:
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
我是否监督某事?
答案 0 :(得分:4)
您在版本中A|B
遗漏了测试flags
。如果测试失败,您将对C
进行归零,而不是将其保留为未修改状态。请记住,如果A
或B
中存在Add TRANSFER_BIT if missing (implied)
或flags |= (flags & (A|B)) ? C : 0
,则可以通过设置标记flags = (flags & (A|B)) ? flags|C : flags
来确定其他位中的其他设置标志是否不受影响。 (请参阅您关联的spotted by MichaelPetch中的评论:flags
)
只需替换boolean-multiply hack的等效三元运算符为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
。
另一种方法是and
,编译得更好。
我在Godbolt上修改了你的代码以使用正确的表达式,是的,它用gcc / clang和MSVC编译成更好看的asm。可能在使用test
做更多内容的更大函数的情况下仍然如此。
看起来像这个版本the original source:
mov
与MSVC类似,但and
代替call
,而mov edx, edi
因为破坏性cmov
而增加*
。
Clang 6.0还消除了冗余呼叫
$ rails db:drop db:create db:migrate
是什么?此代码中没有函数调用。你的意思是clang避免了gcc使用的冗余 let session = await authenticationService.createSessionByEmailPassword(model.email, model.oldpassword), // <--- Note comma here
secuity_ok = !!session;
吗?是的,gcc在那里很蠢。
也许他们过去的经验是旧的编译器没有用三元运算符做得很好,或者没有secuity_ok
的非x86平台并且实际上使用let
运算符源是获得无分支asm的唯一方法之一。
我的猜测是不久之前曾写过这个技巧的人,并且仍然使用它而不是三元运算符。这不是一个好主意,并且没有导致更好的代码。