6

Another question discusses the legitimacy for the optimizer to remove calls to new: Is the compiler allowed to optimize out heap memory allocations?. I have read the question, the answers, and N3664.

From my understanding, the compiler is allowed to remove or merge dynamic allocations under the "as-if" rule, i.e. if the resulting program behaves as if no change was made, with respect to the abstract machine defined in the standard.

I tested compiling the following two-files program with both clang++ and g++, and -O1 optimizations, and I don't understand how it is allowed to to remove the allocations.

// main.cpp
#include <cstdio>

extern int g_alloc;

static int* foo(int n)
{
  // operator new is globally overridden in the other file.
  return new int(n);
}

int main(int argc, char** argv)
{
  foo(argc);
  foo(argc*2);
  printf("allocated: %d\n", g_alloc);
  return g_alloc;
}
// new.cpp
#include <cstdio>
#include <cstdlib>
#include <new>

int g_alloc = 0;

void* operator new(size_t n)
{
  g_alloc += n;
  printf("new %lu\n", n);
  return malloc(n);
}

The idea is to override the default operator new(size_t) with a function with side effects: printing a message and modifying a global variable, the latter being used as the exit code.

The side effects do not impact the allocation itself but it still change the output of the program. Indeed, when compiled without optimizations, the output is:

new 4
new 4
allocated: 8

But as soon as optimizations are enabled, the output is:

allocated: 0

The result is the same with when using standards 98 to 17.

How is the compiler allowed to omit the allocations here? How does it fit the as-if rule?

Chris
  • 26,361
  • 5
  • 21
  • 42
Julien
  • 2,139
  • 1
  • 19
  • 32
  • 2
    Some routines are not allowed to have side effects (or at least not have the side effects honored), because the compiler is permitted to elide them. – Eljay Jan 09 '22 at 20:28
  • @J... Yes, it is mentioned by the asker. I think the whole point of this question is that he didn't realize that being allowed to omit the allocation includes being allowed to change the observable behavior. This should maybe be made more clear in the answers there. – user17732522 Jan 09 '22 at 20:42

1 Answers1

10

Allocation elision is an optimization that is outside of and in addition to the as-if rule. Another optimization with the same properties is copy elision (not to be confused with mandatory elision, since C++17): Is it legal to elide a non-trivial copy/move constructor in initialization?.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Thanks. The cppreference page about allocations indeed states "New-expressions are allowed to elide or combine allocations […]" but it is for c++14 and later. The page about copy elision confirms that allocation elision can change the observable side effects, but again the feature is c++14 or later. Any reference to the standard would be welcome. – Julien Jan 09 '22 at 21:13
  • 1
    It's as simple as that the compiler authors didn't bother disabling the optimization in C++11 (or earlier) mode. You can see in https://gcc.gnu.org/pipermail/gcc-patches/2019-July/525038.html that they simply didn't consider the question. It's true that this means that g++ is not strictly conforming to C++11, but no-one really cares (and if you do you can use fno-allocation-dce to disable the optimization). – ecatmur Jan 09 '22 at 22:19