I have a class offset_ptr that works like a pointer but stores the memory address it points to as offset to its own address this. Here is a version with everything removed that's not required to demonstrate the problem:
template <typename T>
struct offset_ptr {
using offset_t = int64_t;
static constexpr auto const NULLPTR_OFFSET =
std::numeric_limits<offset_t>::max();
offset_ptr(T const* p)
: offset_{p == nullptr ? NULLPTR_OFFSET
: static_cast<offset_t>(
reinterpret_cast<uint8_t const*>(p) -
reinterpret_cast<uint8_t const*>(this))} {}
T* get() {
return
offset_ == NULLPTR_OFFSET
? nullptr
: reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(this) + offset_);
}
offset_t offset_;
};
This code does not work with GCC -O2 and -O3:
int* get() {
offset_ptr<int> ptr = static_cast<int*>(malloc(sizeof(int)));
auto p = ptr.get();
*p = 110; // WOW - please do not optimize me away :-(
return p;
}
(memory management and error checking intentionally omitted to keep it simple!)
This is also visible in the generated assembly: https://godbolt.org/z/PfZEJM
The assignment is just missing.
As shown in the Godbolt Compiler Explorer link above it works when
- the assigned value is used directly in the function itself
- the
offset_ptris located on the heap, not on the stack - no
offset_ptris used at all
It works for:
- Clang (with and without optimizations)
- MSVC (Debug and Release mode)
- GCC (current as well as old versions)
-O0and-O1(but NOT for-O2and-O3)
GCC and Clang Address and UB sanitizer builds do not indicate any problems (besides the leaked memory) when executed.
Can someone point out a section in the C++ standard document that says that there is UB in this code (which could be the reason for GCC aggressively optimizing out the assignment)? Or is it a bug in GCC?
Aucun commentaire:
Enregistrer un commentaire