jeudi 19 avril 2018

GCC C++11/14 and RVO in return statement with parentheses

I'm observing a situation when depending on -std=c++11 or -std=c++14/1z flags gcc (both 5.4 and 7.3) either (my understanding) applies RVO or does not in the case when there are parentheses around return statement expression. clang seems to emit same code that uses RVO in both cases.

Please, consider the following:

#include <iostream>

class C {
  public:
    C(){ std::cout << "ctor" << std::endl; }
    C(C const &){ std::cout << "copy ctor" << std::endl; }
    C(C &&){ std::cout << "move ctor" << std::endl; }
    C & operator=(C const &){ std::cout << "copy =" << std::endl; return *this; }
    C & operator=(C &&){ std::cout << "move =" << std::endl; return *this; }
    ~C() { std::cout << "dtor" << std::endl; }
};

C f1() { C c; return c; }

C f2() { C c; return (c); } // <--- added parentheses;

int main() {
  C c1{}, c2{};

  std::cout << "--- f1 ---" << std::endl;
  c1 = f1();
  std::cout << "--- f2 ---" << std::endl;
  c2 = f2();
  std::cout << "---" << std::endl;
}

For g++ -std=c++11 -O3 -Wall -Wextra -Wpedantic the output is:

ctor
ctor
--- f1 ---
ctor
move =
dtor
--- f2 ---
ctor
move =
dtor
---
dtor
dtor

For g++ -std=c++14 -O3 -Wall -Wextra -Wpedantic the output is:

ctor
ctor
--- f1 ---
ctor
move =
dtor
--- f2 ---
ctor
move ctor
dtor
move =
dtor
---
dtor
dtor

Here is the relevant assembler for f1() and f2() (see it on godbolt.org):

f1():
  push rbp
  mov rbp, rsp
  sub rsp, 16
  mov QWORD PTR [rbp-8], rdi
  mov rax, QWORD PTR [rbp-8]
  mov rdi, rax
  call C::C()
  nop
  mov rax, QWORD PTR [rbp-8]
  leave
  ret
f2():
  push rbp
  mov rbp, rsp
  push rbx
  sub rsp, 40
  mov QWORD PTR [rbp-40], rdi
  lea rax, [rbp-17]
  mov rdi, rax
  call C::C()
  lea rdx, [rbp-17]
  mov rax, QWORD PTR [rbp-40]
  mov rsi, rdx
  mov rdi, rax
  call C::C(C&&)
  nop
  lea rax, [rbp-17]
  mov rdi, rax
  call C::~C()
  jmp .L12
  mov rbx, rax
  lea rax, [rbp-17]
  mov rdi, rax
  call C::~C()
  mov rax, rbx
  mov rdi, rax
  call _Unwind_Resume

And the question is, basically, what's going on? Why does RVO (?) breaks on return statement parentheses in gcc -std=c++14/1z? I know about decltype(auto) x4d = (i); // decltype(x4d) is int& situation, but this shouldn't be the case here.

The implications are kind of grave, considering how much code might be using parentheses and be switching from c++11 to c++14 compilation flags.

Thanks!

Aucun commentaire:

Enregistrer un commentaire