Can compilers optimize pure zero-bit shifts?

I have code similar to the following block of code (I am not allowed to publish the source code) inside the .cpp file, which I think compiled by clang++ ( Ubuntu clang version 3.5.2-3ubuntu1 (tags/RELEASE_352/final) (based on LLVM 3.5.2) ).
It looks like C code because we use GoogleTest to test our C code. Anyway:

 size_t const SHIFT = 4; uint8_t var, var2; /* Omitted: Code that sets var to, say 00011000 (base 2) */ var2 = var; var = var << SHIFT >> SHIFT; // [1] result is 00011000 (base 2) (tested with printf) var2 = var2 << SHIFT; var2 = var2 >> SHIFT; // [2] result is 00001000 (base 2) (tested with printf) 

Now, why is comment [1] true? I assumed that the corresponding line would result in the nullification of the upper 4 bits. But I found out that this is not true; the program simply restores the original value.

Is this a language-specific behavior or is clang compiling a supposedly useless bit shift?

(I checked associativity (using this table on cppreference.com , assuming that the associativity / priority of the main operators will not differ between C++ versions and probably not between C++ and C , or at least not in the β€œcurrent versions”) , and it seems that the RHS expression in [1] should really give the same result as the following two statements)

+7
c ++ c compiler-optimization compilation clang ++
source share
2 answers

What you see is the result of entire promotions . Any value with a type of a lower rank than int , when used in an expression, is incremented to int .

This is the detailed information in section 6.3.1.1 Standard C

2 The following expressions may be used in an expression: int or unsigned int :

  • An object or expression with an integer type (except int or unsigned int ) whose integer conversion rank is less than or equal to the ranks of int and unsigned int .
  • Bit field of type _Bool , int , signed int or unsigned int .

If int can represent all values ​​of the original type (limited by width, for a bit field), the value is converted to int ; otherwise, it is converted to unsigned int . They are called whole stocks. All other types do not change whole promotions.

In this case:

 var = var << SHIFT >> SHIFT; 

var first rises to int . This type has a width of at least 16 bits and is most likely 32 bits wide. So the value that works is 0x00000018 . Left shift 4 leads to 0x00000180 , and then right shift leads to 0x00000018 .

Then the result is stored in uint_8 . Since the value fits into a variable of this type, no conversion is required and 0x18 saved.

+11
source share

Expression:

 var = var << SHIFT >> SHIFT; 

is not semantically equivalent to

 var = var << SHIFT ; var = var >> SHIFT ; 

This will require:

 var = (uint8_t)(var << SHIFT) >> SHIFT ; 

which illustrates the effective difference between the two - the observed behavior does not require optimization, but requires a definition of the language in relation to the rules for promoting types in expressions.

However, it is possible that the compiler can optimize shifts. Optimization cannot change the result when the behavior is defined as it is in this case.

+1
source share

All Articles