Home » C++ » Can the compiler optimize from heap to stack allocation?

Can the compiler optimize from heap to stack allocation?

Posted by: admin November 30, 2017 Leave a comment

Questions:

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?

Answers:

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.

Questions:
Answers:

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

Questions:
Answers:

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.