I want ot know if RVO is enable by default for C. I know that the "-fno-elide-constructors" flag is not enable for gcc but maybe I have a miss on this feature.
Basically, I try to compile this two code :
// main.c :
typedef struct C {
int a;
int b;
int c;
} C;
C f() {
C a;
a.a = 333;
a.b = 444;
a.c = 555;
return a;
}
C g() { return f(); }
int main() {
C obj = g();
obj.a = 666;
obj.b = 777;
obj.c = 888;
}
And this one :
// main.cpp :
struct C {
C() {}
C(const C&) {}
int a;
int b;
int c;
};
C f() {
C a;
a.a = 333;
a.b = 444;
a.c = 555;
return a;
}
C g() { return f(); }
int main() {
C obj = g();
obj.a = 666;
obj.b = 777;
obj.c = 888;
}
I compile both code with the same way :
gcc main.c -S -o c_main.s
g++ main.cpp -S -o cpp_main.s
I have this asm :
.file "main.c"
.text
.globl f
.def f; .scl 2; .type 32; .endef
.seh_proc f
f:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $16, %rsp
.seh_stackalloc 16
.seh_endprologue
movq %rcx, 16(%rbp)
movl $333, -12(%rbp)
movl $444, -8(%rbp)
movl $555, -4(%rbp)
movq 16(%rbp), %rax
movq -12(%rbp), %rdx
movq %rdx, (%rax)
movl -4(%rbp), %edx
movl %edx, 8(%rax)
movq 16(%rbp), %rax
addq $16, %rsp
popq %rbp
ret
.seh_endproc
.globl g
.def g; .scl 2; .type 32; .endef
.seh_proc g
g:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
movq %rcx, 16(%rbp)
movq 16(%rbp), %rax
movq %rax, %rcx
call f
movq 16(%rbp), %rax
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.def __main; .scl 2; .type 32; .endef
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $48, %rsp
.seh_stackalloc 48
.seh_endprologue
call __main
leaq -12(%rbp), %rax
movq %rax, %rcx
call g
movl $666, -12(%rbp)
movl $777, -8(%rbp)
movl $888, -4(%rbp)
movl $0, %eax
addq $48, %rsp
popq %rbp
ret
.seh_endproc
.ident "GCC: (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0"
And for the cpp :
.file "main.cpp"
.text
.section .text$_ZN1CC1Ev,"x"
.linkonce discard
.align 2
.globl _ZN1CC1Ev
.def _ZN1CC1Ev; .scl 2; .type 32; .endef
.seh_proc _ZN1CC1Ev
_ZN1CC1Ev:
.LFB2:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
.seh_endprologue
movq %rcx, 16(%rbp)
nop
popq %rbp
ret
.seh_endproc
.text
.globl _Z1fv
.def _Z1fv; .scl 2; .type 32; .endef
.seh_proc _Z1fv
_Z1fv:
.LFB6:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
movq %rcx, 16(%rbp)
movq 16(%rbp), %rcx
call _ZN1CC1Ev
movq 16(%rbp), %rax
movl $333, (%rax)
movq 16(%rbp), %rax
movl $444, 4(%rax)
movq 16(%rbp), %rax
movl $555, 8(%rax)
nop
movq 16(%rbp), %rax
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.globl _Z1gv
.def _Z1gv; .scl 2; .type 32; .endef
.seh_proc _Z1gv
_Z1gv:
.LFB7:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
movq %rcx, 16(%rbp)
movq 16(%rbp), %rax
movq %rax, %rcx
call _Z1fv
movq 16(%rbp), %rax
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.def __main; .scl 2; .type 32; .endef
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
.LFB8:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $48, %rsp
.seh_stackalloc 48
.seh_endprologue
call __main
leaq -12(%rbp), %rax
movq %rax, %rcx
call _Z1gv
movl $666, -12(%rbp)
movl $777, -8(%rbp)
movl $888, -4(%rbp)
movl $0, %eax
addq $48, %rsp
popq %rbp
ret
.seh_endproc
.ident "GCC: (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0"
For the cpp assembly, you can remove the overhead 20 first line and you have the same number line for c asm and c++ asm.
I'm not very fluent with assembly code but with this result, I think I can say that the RVO is enable for C.
To be sure, if I compile C++ code with -fgcc no-elide-constructors I have so much more assembly line. This is why I think my struct instanciate in the 'f' function is not "copied" in C and 'construct' just one time. At the g assembly function, we clearly can see that there are no manipulation with the 'C' field structure.
Am I wrong ?
Edit : compile with -O0 give the same assembly for main.c.