Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will compilers optimize double logical negation in conditionals?

Tags:

c

optimization

Consider the following hypothetical type:

typedef struct Stack {
    unsigned long len;
    void **elements;
} Stack;

And the following hypothetical macros for dealing with the type (purely for enhanced readability.) In these macros I am assuming that the given argument has type (Stack *) instead of merely Stack (I can't be bothered to type out a _Generic expression here.)

#define stackNull(stack) (!stack->len)
#define stackHasItems(stack) (stack->len)

Why do I not simply use !stackNull(x) for checking if a stack has items? I thought that this would be slightly less efficient (read: not noticeable at all really, but I thought it was interesting) than simply checking stack->len because it would lead to double negation. In the following case:

int thingy = !!31337;
printf("%d\n", thingy);
if (thingy)
    doSomethingImportant(thingy);

The string "1\n" would be printed, and It would be impossible to optimize the conditional (well actually, only impossible if the thingy variable didn't have a constant initializer or was modified before the test, but we'll say in this instance that 31337 is not a constant) because (!!x) is guaranteed to be either 0 or 1.

But I'm wondering if compilers will optimize something like the following

int thingy = wellOkaySoImNotAConstantThingyAnyMore();
if (!!thingy)
    doSomethingFarLessImportant();

Will this be optimized to actually just use (thingy) in the if statement, as if the if statement had been written as

if (thingy)
    doSomethingFarLessImportant();

If so, does it expand to (!!!!!thingy) and so on? (however this is a slightly different question, as this can be optimized in any case, !thingy is !!!!!thingy no matter what, just like -(-(-(1))) = -1.)

In the question title I said "compilers", by that I mean that I am asking if any compiler does this, however I am particularly interested in how GCC will behave in this instance as it is my compiler of choice.

like image 259
A_User Avatar asked Dec 09 '14 19:12

A_User


1 Answers

This seems like a pretty reasonable optimization and a quick test using godbolt with this code (see it live):

#include <stdio.h>

void func( int x)
{  
  if( !!x )
  {
    printf( "first\n" ) ;
  }

  if( !!!x )
  {
    printf( "second\n" ) ;
  }
}

int main()
{
  int x = 0 ;

  scanf( "%d", &x ) ;
  func( x ) ;
}

seems to indicate gcc does well, it generates the following:

func:
    testl   %edi, %edi  # x
    jne .L4 #,
    movl    $.LC1, %edi #,
    jmp puts    #
.L4:
    movl    $.LC0, %edi #,
    jmp puts    #

we can see from the first line:

testl   %edi, %edi  # x

it just uses x without doing any operations on it, also notice the optimizer is clever enough to combine both tests into one since if the first condition is true the other must be false.

Note I used printf and scanf for side effects to prevent the optimizer from optimizing all the code away.

like image 147
Shafik Yaghmour Avatar answered Oct 02 '22 15:10

Shafik Yaghmour