Can the compiler optimize from heap to stack allocation?

C++Memory ManagementLanguage LawyerCompiler Optimization

C++ Problem Overview


As far as compiler optimizations go, is it legal and/or possible to change a heap allocation to a stack allocation? Or would that break the as-if rule?

For example, say this is the original version of the code

{
    Foo* f = new Foo();
    f->do_something();
    delete f;
}

Would a compiler be able to change this to the following

{
    Foo f{};
    f.do_something();
}

I wouldn't think so, because that would have implications if the original version was relying on things like custom allocators. Does the standard say anything specifically about this?

C++ Solutions


Solution 1 - C++

Yes, it's legal. expr.new/10 of C++14:

> An implementation is allowed to omit a call to a replaceable global > allocation function (18.6.1.1, 18.6.1.2). When it does so, the storage > is instead provided by the implementation or provided by extending the > allocation of another new-expression.

expr.delete/7:

> If the value of the operand of the delete-expression is not a null > pointer value, then: > > — If the allocation call for the new-expression for the object to be > deleted was not omitted and the allocation was not extended (5.3.4), > the delete-expression shall call a deallocation function (3.7.4.2). > The value returned from the allocation call of the new-expression > shall be passed as the first argument to the deallocation function. > > — Otherwise, if the allocation was extended or was provided by > extending the allocation of another new- expression, and the > delete-expression for every other pointer value produced by a > new-expression that had storage provided by the extended > new-expression has been evaluated, the delete-expression shall call a > deallocation function. The value returned from the allocation call of > the extended new-expression shall be passed as the first argument to > the deallocation function. > > — Otherwise, the delete-expression will not call a deallocation > function (3.7.4.2).

So, in summary, it's legal to replace new and delete with something implementation defined, like using the stack instead of heap.

Note: As Massimiliano Janes comments, the compiler could not stick exactly to this transformation for your sample, if do_something throws: the compiler should omit destructor call of f in this case (while your transformed sample does call the destructor in this case). But other than that, it is free to put f into the stack.

Solution 2 - C++

These are not equivalent. f.do_something() might throw, in which case the first object remains in memory, the second gets destructed.

Solution 3 - C++

I'd like to point out something IMO not stressed enough in the other answers:

struct Foo {
	static void * operator new(std::size_t count) {
		std::cout << "Hey ho!" << std::endl;
		return ::operator new(count);
	}
};

An allocation new Foo() cannot generally be replaced, because:

> An implementation is allowed to omit a call to a replaceable global allocation function (18.6.1.1, 18.6.1.2). When it does so, the storage is instead provided by the implementation or provided by extending the allocation of another new-expression.

Thus, like in the Foo example above, the Foo::operator new needs to be called. Omitting this call would change the observable behavior of the program.

Real world example: Foos might need to reside in some special memory region (like memory mapped IO) to function properly.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCory KramerView Question on Stackoverflow
Solution 1 - C++gezaView Answer on Stackoverflow
Solution 2 - C++lorroView Answer on Stackoverflow
Solution 3 - C++Daniel JourView Answer on Stackoverflow