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

            Bug ID: 83032
           Summary: Copy elision for returning subobject
           Product: gcc
           Version: 8.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: middle-end
          Assignee: unassigned at gcc dot gnu.org
          Reporter: antoshkka at gmail dot com
  Target Milestone: ---

Following code

struct trace {
    trace(trace const &) noexcept;
    ~trace();
};

struct ts { trace a, b; };
ts foo();

trace testing() {
    return foo().a;
}


Compiles into assembly that calls copy constructor and destructors for `trace`
objects returned from `foo()`.

This could be optimized: return from `foo()` already puts `a` into a correct
place for return from `testing()`. So all we need is no call destructor for
`b`.

Optimal assembly should look close to the following:
testing():
        push    rbx
        mov     rbx, rdi
        sub     rsp, 16
        call    foo()
        lea     rdi, [rsp+15]
        call    trace::~trace()
        add     rsp, 16
        mov     rax, rbx
        pop     rbx
        ret

Current suboptimal assembly looks like:
testing():
        push    rbx
        mov     rbx, rdi
        sub     rsp, 16
        lea     rdi, [rsp+14]
        call    foo()
        lea     rsi, [rsp+14]
        mov     rdi, rbx
        call    trace::trace(trace const&)   <== Avoid this
        lea     rdi, [rsp+15]
        call    trace::~trace()
        lea     rdi, [rsp+14]
        call    trace::~trace()              <== Avoid this
        add     rsp, 16
        mov     rax, rbx
        pop     rbx
        ret

This optimization is very useful for C++ code, where returning std::pair,
std::tuple, std::variant or std::optional is a common practice. For example
following code could use copy elision for std::string inside std::optional:

std::optional<std::string> foo();
std::string testing() {
    return *foo(); // No copy/move constructor call
}

Reply via email to