4

The example code is quite simple, with a simple custom allocator and a std::vector:

#include <cstdlib>
#include <vector>

namespace {

template <typename T>
class MyAllocator {
 public:
  typedef T value_type;

  T* allocate(std::size_t n) {
    T* p = static_cast<T*>(std::malloc(sizeof(T) * n));
    if (p == nullptr) throw std::bad_alloc();
    return p;
  }

  void deallocate(T* p, std::size_t) {
    std::free(p);
  }
};

void bar() {
  std::vector<int, MyAllocator<int>> v;
  v.push_back(49);
}

}  // anonymous namespace

int main(int, char**) {
  ::bar();
  return 0;
}

Clang 6.0.0 can "full optimise" the code removing the allocation. GodBolt Here:

main: # @main
  xor eax, eax
  ret

Is that legal? Can a std::malloc be discarded by the optimizer, even if there is a throw flow?

Note that GCC gives me the expected result.


Bonus Question: if I remove the anonymous namespace the compiler produces 150 lines of assembly code, why?

M.M
  • 138,810
  • 21
  • 208
  • 365
BiagioF
  • 9,368
  • 2
  • 26
  • 50
  • 1
    *"Is that legal?"* Yes, and there is definitely a dupe for that. Do you want to make your bonus question the main question? If so, note that the assembly of `main` is probably the only interesting point there. – Baum mit Augen May 13 '18 at 11:17
  • @BaummitAugen Optimisations should never change the behaviour of the program. If I am not wrong, in that case, the "exception execution flow" is dropped by the compiler. – BiagioF May 13 '18 at 11:25
  • 7
    Never, unless there's an explicit exception. This and RVO are two. See [Is the compiler allowed to optimize out heap memory allocations?](//stackoverflow.com/a/31875487) – Baum mit Augen May 13 '18 at 11:27
  • 2
    You've shown main function disassembly, do you ever all `bar` function? If not, it may be discarded. Annonymous namespace may help discarding, as the function cannot be alled from other translation unit – Alex Guteniev May 13 '18 at 11:35
  • `bar` is called - it even inlines `.size()` afterwards: https://godbolt.org/g/XsXAfM – hlt May 13 '18 at 11:36
  • You don't even need special permission. malloc is a standard function, if the implementation is able to guarantee that it won't return 0 (in this case by allocating on the stack), it can remove that path. – Marc Glisse May 13 '18 at 12:35
  • 1
    @BaummitAugen But is `malloc` a replaceable global allocation function? – xskxzr May 13 '18 at 12:39
  • @BaummitAugen Even with `-std=c++11` clang optimises it – BiagioF May 13 '18 at 13:46
  • @BiagioFesta Yes, that's clang being non-compliant. – Baum mit Augen May 13 '18 at 13:48
  • I think this is duplicate of the question linked to by Baum. The answer is "Yes" (this optimization is legal) as explained by those answers – M.M May 14 '18 at 04:42
  • If you don't put the allocator in a namespace then it has external linkage and the compiler has to allow for another translation unit calling its functions (the godbolt compiler builds translation units, not whole programs) – M.M May 14 '18 at 04:43
  • As I see the call of bar() did really nothing! You generate a vector and push a value which is never used anymore. So I expect that the compiler will drop the call to bar() because it is useless. – Klaus May 14 '18 at 05:27
  • @BiagioFesta yes as I [pointed out in my answer here](https://stackoverflow.com/a/31877074/1708801) clang implemented this first and then it was standardized. – Shafik Yaghmour Jul 09 '18 at 05:47
  • @M.M any reason not to close as a duplicate? – Shafik Yaghmour Jul 09 '18 at 05:47
  • @ShafikYaghmour go ahead – M.M Jul 09 '18 at 07:00

0 Answers0