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

            Bug ID: 97562
           Summary: NRVO is very fragile (adding an extra scope breaks the
                    optimization)
           Product: gcc
           Version: 10.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: barry.revzin at gmail dot com
  Target Milestone: ---

Consider the following (where T is just some type that is trivially copyable
but large enough):

struct T {
    char _[24];
};

bool good_enough(T const&);
T get();

T as_loop() {
    for (;;) {
        T obj = get();
        if (good_enough(obj)) {
            return obj;
        }
    }
}

T as_goto() {
    loop:
        T obj = get();
        if (good_enough(obj)) {
            return obj;
        }
        goto loop;
}

The two functions, as_loop and as_goto, are semantically equivalent. Yet they
emit rather different code. With gcc 10.2 -O3 (https://godbolt.org/z/5dx3od),
as_loop is:

as_loop():
        push    r12
        mov     r12, rdi
        sub     rsp, 32
.L3:
        mov     rdi, rsp
        call    get()
        mov     rdi, rsp
        call    good_enough(T const&)
        test    al, al
        je      .L3
        mov     rax, QWORD PTR [rsp+16]
        movdqu  xmm0, XMMWORD PTR [rsp]
        mov     QWORD PTR [r12+16], rax
        mov     rax, r12
        movups  XMMWORD PTR [r12], xmm0
        add     rsp, 32
        pop     r12
        ret

while as_goto is:

as_goto():
        push    r12
        mov     r12, rdi
.L8:
        mov     rdi, r12
        call    get()
        mov     rdi, r12
        call    good_enough(T const&)
        test    al, al
        je      .L8
        mov     rax, r12
        pop     r12
        ret

That is, the goto implementation gives us NRVO, but the loop implementation
does not. This has something to do with the extra scope. Adding an extra set of
braces, as in:

T as_goto() {
    {
    loop:
        T obj = get();
        if (good_enough(obj)) {
            return obj;
        }
        goto loop;
    }
}

emits the same code as the loop example.

Reply via email to