https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120894

            Bug ID: 120894
           Summary: Usage of std::pmr::polymorphic_allocator<T> is not
                    optimized out
           Product: gcc
           Version: 16.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: m.cencora at gmail dot com
  Target Milestone: ---

Following program is not fully optimized (to 16-byte stack allocation + zeroing
and a call to 'use').

$ cat pmr.cpp

#include <vector>
#include <memory_resource>

#ifdef WORKAROUND
namespace std::pmr {
[[gnu::pure]] memory_resource* get_default_resource() noexcept;
memory_resource::~memory_resource() {}
}
#endif

void use(const std::byte*) noexcept;

namespace
{
struct StaticResource final : public std::pmr::memory_resource
{
    constexpr StaticResource(void* buffer, std::size_t buffer_size,
std::pmr::memory_resource* upstream = std::pmr::get_default_resource())
noexcept
        : buffer{buffer}, buffer_left{buffer_size}, upstream{upstream}
    {
    }

    void* do_allocate(std::size_t bytes, std::size_t alignment =
alignof(std::max_align_t)) override
    {
        if (auto ret = static_cast<std::byte*>(std::align(alignment, bytes,
buffer, buffer_left)))
        {
            buffer_left -= bytes;
            buffer = &ret[bytes];
            return ret;
        }
        else
        {
            return upstream->allocate(bytes, alignment);
        }
    }

    void do_deallocate(void*, std::size_t, std::size_t =
alignof(std::max_align_t)) override {}

    bool do_is_equal(const memory_resource& other) const noexcept override {
return &other == this; }

private:
    void* buffer;
    std::size_t buffer_left;
    std::pmr::memory_resource* upstream;
};

}

void test()
{
    std::byte buf[16];
    StaticResource res(&buf[0], sizeof(buf));
    std::pmr::vector<std::byte> vec{16, &res};
    use(vec.data());
}

$ g++ -std=c++20 -O3 pmr.cpp

Resulting assembly still have non-inlined calls to StaticResource::do_allocate,
unused call std::pmr::get_default_resource and invocation of no-op destructor
of std::pmr::memory_resource.

See here: https://godbolt.org/z/br57EaGhn

This can be improved somewhat by compilation with additional -DWORKAROUND flag,
but the calls to std::pmr::get_default_resource and StaticResource::do_allocate
are still there.

"test()":
        sub     rsp, 72
        movq    xmm0, QWORD PTR .LC0[rip]
        lea     rax, [rsp+16]
        movq    xmm1, rax
        punpcklqdq      xmm0, xmm1
        movaps  XMMWORD PTR [rsp], xmm0
        call    "std::pmr::get_default_resource()"
        movdqa  xmm0, XMMWORD PTR [rsp]
        lea     rdi, [rsp+32]
        mov     edx, 1
        mov     esi, 16
        mov     QWORD PTR [rsp+56], rax
        movaps  XMMWORD PTR [rsp+32], xmm0
        mov     QWORD PTR [rsp+48], 16
        call    "(anonymous namespace)::StaticResource::do_allocate(unsigned
long, unsigned long)"
        pxor    xmm0, xmm0
        movups  XMMWORD PTR [rax], xmm0
        mov     rdi, rax
        call    "use(std::byte const*)"
        add     rsp, 72
        ret

clang with -O2 -DWORKAROUND does pretty good job:
test():
        sub     rsp, 24
        xorps   xmm0, xmm0
        movaps  xmmword ptr [rsp], xmm0
        mov     rdi, rsp
        call    use(std::byte const*)@PLT
        add     rsp, 24
        ret

Obviously marking the std::pmr::get_default_resource as 'pure' function is
wrong, so it would be great if we had an attribute that indicates functions
does
not modify program state, but cannot be reordered or CSE-d.

Also not sure why but ~memory_resource is not-defined inline, and most likely
we
cannot change it due to ABI so we would need another attribute that states the
function is a no-op.

Reply via email to