Why does the C ++ compiler not eliminate the null check on the pointer returned by the new one?

I recently ran the following code on ideone.com (gcc-4.3.4)

#include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <new> using namespace std; void* operator new( size_t size ) throw(std::bad_alloc) { void* ptr = malloc( 2 * 1024 * 1024 * 1024); printf( "%p\n", ptr ); return ptr; } void operator delete( void* ptr ) { free( ptr ); } int main() { char* ptr = new char; if( ptr == 0 ) { printf( "unreachable\n" ); } delete ptr; } 

and got this conclusion:

 (nil) unreachable 

although new should never return a null pointer, and therefore the caller could count on it, and the compiler could exclude the ptr == 0 check and treat the dependent code as unreachable.

Why doesn't the compiler eliminate this code? Is it just a missed optimization, or is there some other reason for this?

+6
source share
7 answers

I think you expect too much optimizer. By the time the optimizer goes to this code, he believes that new char is just another function call, the return value of which is stored on the stack. Therefore, he does not see the if condition as deserving special treatment.

This is probably due to the fact that you overloaded operator new , and it is outside the pay rating for the optimizer, to look there, see the name malloc , which can return NULL , and decide that this overridden version won 't return NULL . malloc looks like another function call. Who knows? You may also be referring to your own version.

There are several more examples of overridden operators that change their behavior in C ++: operator && , operator || and operator , Each of them has a special behavior if it is not redefined, but when redefined, they behave like standard operators. For example, operator && will not calculate the right side at all if the left side evaluates to false . However, if overridden, both sides of operator && are computed before passing them to operator && ; the short circuit function is completely disabled. (This is done to support operator overloading for defining mini-languages ​​in C ++, for example, see the Boost Spirit library.)

+4
source

I think this is very simple, and you have two fundamentally different things, confused:

  • malloc () can return anything, in particular zero.

  • C ++ standard distribution function void * operator new(size_t) throw(std::bad_alloc) is required by the standard in order to either return a pointer to the required amount of memory (+ appropriately aligned) or otherwise exit the exception.

If you want to replace the global distribution function, it is your responsibility to provide a replacement that complies with the rules of the standard. The simplest version looks like this:

 void * operator new(size_t n) throw(std::bad_alloc) { void * const p = std::malloc(n); if (p == NULL) throw std::bad_alloc(); return p; } 

Any serious implementation should actually contain a loop to call the registered new handler until the distribution is successful, and just throw it away when there are no more new handlers.

The program you wrote is simply poorly formed.


Digression: why is this new defined this way? Consider the standard distribution sequence when you say T * p = ::new T(); . This is equivalent to this:

 void * addr = ::operator new(sizeof(T)); // allocation T * p = ::new (addr) T(); // construction 

If the second line throws away (i.e. the design does not work), the memory is freed using the corresponding deallocation function. However, if the first call failed, the execution should not reach the second line! The only way to achieve this is to get out of the exception. (Distribution options without throwing are intended only for manual use, when the user code can check the result of the dispenser before proceeding with construction.)

+7
source

C ++ 11 clearly talks about the problem:

void* operator new(std::size_t size); : ... 3 Mandatory behavior: Return a non-null pointer to a suitable aligned memory (3.7.4) or throw a bad_alloc exception. This requirement is required to replace a version of this feature.

You clicked on Undefined Behavior.

[edit] Now, why does this interfere with optimization? Compiler vendors tend to spend their time looking for optimizations for the code templates that are commonly used. They usually have little use for optimization for faster Undefined Behavior. (Some UBs can be well defined on this particular compiler and are still optimized, but the example above probably won't).

+6
source

Why would the compiler do this?

With the opaque implementation of new it is impossible to find out if the implementation is correct or not. Your non-standard, so you are lucky that he still checked.

+3
source

There is more than one operator new ; See here . And you did not declare your only possible exception. Therefore, the compiler should not conclude that it never returns a null pointer.

I do not know very well the latest C ++ 11 standard, but I assume that this is only the standard definition of operator new (exception for exception), which should return a non-nil pointer, and not some user-defined one.

And in the current GCC trunk, the libstdc++-v3/libsupc++/new file does not seem to contain any specific attribute indicating to GCC that nil never returns ... even if I believe that undefined behavior should become null with a new cast .

+2
source

although the new one should never return a null pointer

It should not work in normal mode. But what about a crazy situation where someone closes their memory, or just dies, or just fills up?

Why doesn't the compiler eliminate this code? Is it just a missed optimization or is there some other reason for this?

Because a new one can fail. If you use a version without a throw, it can return NULL (or nulptr in C ++ 11).

0
source

I checked the build made with g ++ -O3 -S, and the standard new gcc (4.4.5) does not remove if(ptr == 0) . Gcc doesn't seem to have compiler flags or function attributes to optimize NULL checks.

So, it seems that gcc does not support this optimization at the current time.

0
source

Source: https://habr.com/ru/post/902003/


All Articles