0

I have o2 compiler flag set. Tested on multiple platforms/compilers. Both variables are not used at all.

const char * temp1 = "a";// optimised away
char * temp2 = new char[1];//not optimised.

First variable is dropped away by compiler since it is redundant. The second variable counterwise. Is compiler unable to detect that this memory chunk is used nowhere in the program?

The original issue comes from real product when I noticed that temporary, unused std::string longer than 15 signs and passed as function's parameter is not optimized away.

asm view online: https://godbolt.org/g/Shmx92

Edit: as Jarod42 mentioned clang optimises both variables away.

Grzesiek Mal
  • 31
  • 1
  • 8
  • 3
    `char * temp1 = "a";` should not compile because of missing `const`. – Jarod42 Jul 12 '18 at 10:37
  • Clang optimizes both [Demo](https://godbolt.org/g/5hThjS). – Jarod42 Jul 12 '18 at 10:39
  • You may find one of my questions useful: https://stackoverflow.com/questions/31873616/is-the-compiler-allowed-to-optimize-out-heap-memory-allocations. Not a duplicate, but explains that optimizing heap allocations is done by some compilers only. – Banex Jul 12 '18 at 10:40
  • 1
    The compiler *can* optimise either case out of existence. It is not *required* to do so, in either case. Practically, it depends on whether the compiler developer is prepared to implement analysis to detect that the variables are not used, and (if a case is detected) transform the compiler output (e.g. object code) to remove them. – Peter Jul 12 '18 at 10:45
  • Actually it's called _free-store_ in standard C++, not _heap_. – Ron Jul 12 '18 at 10:50

2 Answers2

0
char * temp2 = new char[1];

In this case, you call operator new (sizeof(char) * 1) Actually operator new - is declaration of function(vcruntime_new.h library). After compilation 'new char[1]' statement will be replaced on call (asm function address). And linker will bind this call with real address of operator new function from .obj or .dll. That's why compiler just doesn't have a chance to know that do operator new function and it don't optimize this statement.

void foo()
{
    const char* str = "";
}

void buz()
{
    char* str = new char[1];
}

Disassembly of this functions (without optimization):

?foo@@abrakadabra (void __cdecl foo(void)): ; foo's signature
mov dword ptr[ebp - 4], offset $...         ; insted ... offset link to table of symbols
;...

?buz@@abrakadabra (void __cdecl buz(void)): ; buz's signature
call ??_U@...     ; insted ... mnemonic name of operator new function
mov dword ptr [ebp - 4], eax    ; this is simplification

In statement 'call ...' compiler can't to know what really do this function. It's the task for linker.

Remark: some code in assembler was simplified for better understanding

acade
  • 245
  • 2
  • 7
  • I get your point but Clang somehow knows that this variable can be optimised. I think this is still compilation level - see Demo made by Jarod42 above. – Grzesiek Mal Jul 12 '18 at 20:01
0

The first variable

const char * temp1 = "a";// optimised away

is not on the heap but rather points to the read-only data section. Creating a string "a" has no side effects and the compiler can optimize it away safely.

The second variable

char * temp2 = new char[1];//not optimised.

on the other hand calls operator new. That could be overloaded or some malloc hooks could be set in the libc causing side effects. Optimizing this away would change the behavior of the program. The compiler knows when new is overloaded and it might assume it knows what the underlying libc does (clang seems to). But say I do set a malloc hook then the behavior suddenly changed. I think clang is wrong to optimize the call away.

Secondly that code is broken since you leak memory. There should be at least one use of temp2 calling delete. So a case like yours should never ever appear in real code. The optimizer would have to see both the new and delete call and determine:

  1. the pointer is never used anywhere else
  2. delete is always called when new was called

I also find it unlikely that you would allocate memory and then not use it. Why not simply initialize the variable to nullptr?

Optimization usually covers frequently used, sensible code.

Goswin von Brederlow
  • 11,875
  • 2
  • 24
  • 42
  • Mem leak was intended to keep the code short. It doesn't affect described issue. As I mentioned, the issue comes from production code where due to some platform related #ifdefs std::string was redundant in some cases. – Grzesiek Mal Jul 12 '18 at 19:59