#include <experimental/optional>
struct O { bool inited = false; int value;
~O() { if(inited) inited = false; }};
void destruct_optional_int(O& o) { o.~O(); // should be noop ?!}
void more_optional_int() { O o; // address does not leak}
After the lifetime of an object has ended data within its layout has undefined value.
Godbolt to compile code.
#include <experimental/optional>struct O {bool inited = false;int value;~O() {if(inited)inited = false;}};void destruct_optional_int(O& o) {o.~O(); // should be noop ?!}void more_optional_int() {O o; // address does not leak}A few years ago I noticed boost::optional<int> deconstructing with a branch. One could argue such containers should be coded to have trivial construction. It seems the container std::optional that ships with gcc and clang have fixed the issue.I always thought there was room in the language spec to help optimization. I'd like to propose something like this:After the lifetime of an object has ended data within its layout has undefined value.
Often the compiler can figure out that operations on such an object's memory are undefined anyway, if the object existed on the stack or a delete expression releases it to the heap. However in cases where the memory is from a pool or container are or the address leaks out of compilation scope, it may not be able to prove this.
Note that the implementation can optimize further. Once it is known the memory of an object is undefined at a certain point, any previous stores to that memory may be elided as long as they can be shown to have no side effects up until that point.
It looks like gcc is assuming this is in the standard already. Clang and ICC do not. Does anyone know if gcc is right?
On Tue, Apr 17, 2018, 1:47 PM <christophe...@gmail.com> wrote:I always thought there was room in the language spec to help optimization. I'd like to propose something like this:After the lifetime of an object has ended data within its layout has undefined value.But this is already the case. See [basic.life]p1.3 that says that the lifetime of an (class) object ends when the destructor is called and [basic.life]p6.2 which says that non-static member access after the lifetime of the object has ended is undefined behavior.
For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.
void vector_int(std::vector<int>& v) { v.back() *= 0xDEADBEEF; // can't be removed v.pop_back();}
void vector_O(std::vector<O>& v) { v.back().value *= 0xDEADBEEF; v.pop_back();}
void vector_optional(std::vector<std::optional<int>>& v) { *(v.back()) *= 0xDEADBEEF; v.pop_back();}
I've thought about a set_undefined(T&) function that at least for trivial types overwrites them with uninitialized memory and/or calls VALGRIND_MAKE_MEM_UNDEFINED or ASAN_UNPOISON_MEMORY_REGION. If I did this in my code though, there's still the risk that some compiler would actually feel obligated to overwrite the data with junk instead of just taking the hint that this is undefined.
Only vector_O is optimized away. I assume the issue is trivial deconstruction. The vector_optional case is especially annoying since the implementer of optional worked hard to give it trivial deconstruction.
I'm pretty sure the spec for vector says referencing beyond end is undefined. Is there a way for the container implementer to tell the compiler data is undefined? I've wondered if someone has proposed something like that.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHSYqdbn-N-LCqk_%3DyPx%2B43g_Z_MzB4vQk0oYax7uD7O%3DKv1Xg%40mail.gmail.com.
Optimizations are why we use C++ for games. We absolutely need more ways for the compiler to eliminate code, assuming those mechanisms are well specified and clear.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/1abb061a-5da1-4dec-af26-a2f0184543c3%40isocpp.org.
Currently, a destructor call does not end the lifetime of a trivially destructible object. See [basic.life]/p1. Core issue 2256 is going to change that.
volatile int sink=0;
struct A { int& ir; ~A () { sink += ir++; } // Undefined Behavior?};
struct B : A{ int i = 3; B() : A{i} {}};
void bar() { B b; b.i = 7; // May the compiler skip this store?}