You are right, the obvious implementation uses branches. My initial idea was to convert each operand to 0 or -1 through a bit shift with a sign extension, and then using a bitwise and instruction, but gcc (1) reminded me of the set xy x86 instruction, so it looks simpler.
The following function implements: int f(int a, int b) { return a && b; } int f(int a, int b) { return a && b; }
.text .globl f f: cmpl $0, 4(%esp) setne %dl cmpl $0, 8(%esp) setne %al movzbl %al, %eax andl %edx, %eax ret
As it happens, the correct value for the expression can be calculated without branches or transitions, but, defining my implementation as a function, Iβm kind of cheating, because it forces both operands to be evaluated in order to call the function, at that moment the function does not harm by reading both operands without a short circuit, because the parameters are known to be copies.
But for open source, whether this can be done without a transition depends entirely on whether the right operand can be evaluated without side effects. So it depends on the type. Imagine a case where the right operand is a function call, possibly an I / O statement or a kernel system call. The generated code cannot evaluate it at all if the left operand is false, and I donβt see how this can be done without a branch or branch.
Interestingly, gcc -O (generating similar code) seems to violate C99 6.5.13 (4), which reads, "If the first operand is compared to 0, the second operand is not evaluated." In the case of f () assembly, he apparently decides that since the value parameter cannot have side effects, then there is no harm in compiling code that contradicts the specification of the letter C99.