How can you encode the C && operator in x86?

How can I encode the C '& &' operator in x86? For instance:

int a = ... int b = ... int c = a && b; 

What would be the equivalent of the last line in x86?

EDIT: I want to do this without any transitions.

EDIT: g ++ generates this, but I don't get it:

 testl %edi, %edi setne %dl xorl %eax, %eax testl %esi, %esi setne %al andl %edx, %eax 
+4
source share
4 answers

This is how GCC implements it in -O3 .

  movl 8(%esp), %edx ;; load argument into edx xorl %eax, %eax ;; eax = 0 movl 4(%esp), %ecx ;; load other argument into ecx testl %edx, %edx ;; Is EDX nonzero? setne %al ;; al = 1 if Z = 0 xorl %edx, %edx ;; EDX = 0 testl %ecx, %ecx ;; Is ECX nonzero? setne %dl ;; dc = 1 if Z = 0 andl %edx, %eax ;; edx &= eax 

Please note that this code is not shorted; this is due to the fact that in this case, GCC can prove the absence of side effects from the second argument. If the second argument has side effects, you should implement it using transitions. For instance:

 int test(int *a, int *b) { return (*a)++ && (*b)++; } 

becomes:

 test: pushl %ebx ;; save ebx movl 8(%esp), %eax ;; load a into eax movl 12(%esp), %ecx ;; load b in to ecx movl (%eax), %edx ;; *a -> edx leal 1(%edx), %ebx ;; ebx = edx + 1 movl %ebx, (%eax) ;; *a <- ebx xorl %eax, %eax ;; eax = 0 testl %edx, %edx ;; if the old value of *a was 0... je .L2 ;; jump to the end movl (%ecx), %eax ;; *a -> eax testl %eax, %eax ;; does *a = 0? leal 1(%eax), %edx ;; edx = *a + 1 (does not set flags!) setne %al ;; al = 1 if Z (ie, if a = 0 at the testl above) movl %edx, (%ecx) ;; save edx to *a (increment *a) movzbl %al, %eax ;; zero-extend al to eax .L2: popl %ebx ;; restore ebx ret ;; return 
+10
source

You cannot do this without jumping because && is a short circuit operator.

  XOR ecx, ecx MOV eax, <value of A> MOV ebx, <value of B> TEST eax, eax JZ testDone TEST ebx, ebx JZ testDone INC ecx testDone: ... 
+3
source

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.

0
source

This will work (and is the fastest part of the code) only if a and b are normalized, i.e. it contains a boolean value 1 for true or 0 for false.

 MOV eax, <value> ; a = ... MOV ebx, <value> ; b = ... MOV ecx, eax AND ecx, ebx ; c = a & b 
-1
source

All Articles