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.