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

            Bug ID: 123214
           Summary: Performance regression for std::optional
           Product: gcc
           Version: 15.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: kamkaz at windowslive dot com
  Target Milestone: ---

In certain situations, parts of the std::optional seem to be moved to stack
from registers before being used.

#include <optional>
void fusroh(long&a, long&b, std::optional<long> c);
void dah(long&a, long&b, std::optional<long> c) {
    if (c.has_value()) {
        a += *c;
        b += *c;
    }
    fusroh(a, b, c);
}

GCC generates:

dah(long&, long&, std::optional<long>):
        mov     QWORD PTR [rsp-24], rdx
        mov     QWORD PTR [rsp-16], rcx
        cmp     BYTE PTR [rsp-16], 0
        je      .L2
        mov     rax, QWORD PTR [rsp-24]
        add     QWORD PTR [rdi], rax
        add     QWORD PTR [rsi], rax
.L2:
        mov     rdx, QWORD PTR [rsp-24]
        mov     rcx, QWORD PTR [rsp-16]
        jmp     fusroh(long&, long&, std::optional<long>)

Clang generates:

dah(long&, long&, std::optional<long>):
        test    cl, 1
        je      .LBB0_2
        add     qword ptr [rdi], rdx
        add     qword ptr [rsi], rdx
.LBB0_2:
        movzx   ecx, cl
        jmp     fusroh(long&, long&, std::optional<long>)@PLT


In this scenario, both the flag and the value are moved to the stack before
being used. In some scenarios, only value gets moved to the stack. In yet other
ones, everything is processed in registers, as it should.

It seems to be a performance regression in GCC 9.1

I believe it to be of importance, as it might prevent certain code bases to
migrate from (const T*) "optional" parameters to std::optional

Reply via email to