samedi 15 décembre 2018

Why does GCC optimize out assignments here?

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_ptr is located on the heap, not on the stack
  • no offset_ptr is used at all

It works for:

  • Clang (with and without optimizations)
  • MSVC (Debug and Release mode)
  • GCC (current as well as old versions) -O0 and -O1 (but NOT for -O2 and -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