CinCout
CinCout

Reputation: 9619

Compiler optimization for constructors

I am aware of the advantages an initializer list has over normal constructor body (default construction followed by assignment, instead of construction).

I am also aware of the optimization capabilities of modern compilers.

Are the modern compilers smart enough to optimize a non-initializer list constructor to the former type? If yes, is it restricted to just the basic types, or are user-defined types included as well? If not, why not?

Upvotes: 1

Views: 205

Answers (1)

Richard Hodges
Richard Hodges

Reputation: 69864

Here's how gcc5.3 handles it with -O2. Your suspicions are correct - in trivial cases the optimiser makes up for sloppy programming.

The problem occurs when the compiler can't see into the constructor or assignment operator of member variables (in this case, because they are defined in another translation unit).

When that happens, you get better code (at least with GCC and I suspect with all others) if the constructors are written properly:

test code:

#include <string>

struct bar
{
  bar(std::string = {}, std::string = {});
  bar(bar&&);
  bar& operator=(bar&&);
};

struct foo
{
  __attribute__((noinline))
  foo(int x, double y, std::string z, std::string o, std::string p)
  {
    a = x;
    b = y;
    c = z;
    _bar = bar(o, p);
  }
  int a;
  double b;
  std::string c;
  bar _bar;
};

struct foo2
{
  __attribute__((noinline))
  foo2(int x, double y, std::string z, std::string o, std::string p)
  : a(x), b(y), c(std::move(z)), _bar(std::move(o), std::move(p))
  {
  }

  int a;
  double b;
  std::string c;
  bar _bar;
};


int main()
{
  foo f(45, 12.2, "hello", "foo", "bar");
  foo2 f2(45, 12.2, "hello", "foo", "bar");

}

example assembler output:

.LC0:
        .string "basic_string::_M_construct null not valid"
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [clone .isra.16]:
        pushq   %r13
        pushq   %r12
        leaq    16(%rdi), %r12
        pushq   %rbp
        pushq   %rbx
        subq    $24, %rsp
        testq   %rsi, %rsi
        movq    %r12, (%rdi)
        je      .L2
        movq    %rdi, %rbx
        movq    %rsi, %rdi
        movq    %rsi, %r13
        call    strlen
        cmpq    $15, %rax
        movq    %rax, %rbp
        movq    %rax, 8(%rsp)
        ja      .L13
        cmpq    $1, %rax
        je      .L14
        testq   %rax, %rax
        jne     .L15
.L6:
        movq    8(%rsp), %rax
        movq    (%rbx), %rdx
        movq    %rax, 8(%rbx)
        movb    $0, (%rdx,%rax)
        addq    $24, %rsp
        popq    %rbx
        popq    %rbp
        popq    %r12
        popq    %r13
        ret
.L13:
        leaq    8(%rsp), %rsi
        xorl    %edx, %edx
        movq    %rbx, %rdi
        call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long)
        movq    8(%rsp), %rdx
        movq    %rax, (%rbx)
        movq    %rax, %rdi
        movq    %rdx, 16(%rbx)
.L4:
        movq    %rbp, %rdx
        movq    %r13, %rsi
        call    memcpy
        jmp     .L6
.L14:
        movzbl  0(%r13), %eax
        movb    %al, 16(%rbx)
        jmp     .L6
.L2:
        movl    $.LC0, %edi
        call    std::__throw_logic_error(char const*)
.L15:
        movq    %r12, %rdi
        jmp     .L4
foo::foo(int, double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >):
        pushq   %r15
        pushq   %r14
        leaq    32(%rdi), %r14
        pushq   %r13
        pushq   %r12
        leaq    48(%rdi), %r12
        pushq   %rbp
        pushq   %rbx
        movl    %esi, %r15d
        movq    %rdi, %rbx
        movq    %rcx, %r13
        movq    %r8, %rbp
        subq    $104, %rsp
        movq    %r14, 16(%rdi)
        movq    $0, 24(%rdi)
        leaq    80(%rsp), %rax
        movq    %rdx, 8(%rsp)
        leaq    32(%rsp), %rsi
        leaq    64(%rsp), %rdx
        movb    $0, 32(%rdi)
        movq    %r12, %rdi
        movq    %rax, 64(%rsp)
        leaq    48(%rsp), %rax
        movsd   %xmm0, (%rsp)
        movq    $0, 72(%rsp)
        movb    $0, 80(%rsp)
        movq    %rax, 32(%rsp)
        movq    $0, 40(%rsp)
        movb    $0, 48(%rsp)
        call    bar::bar(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
        movq    32(%rsp), %rdi
        leaq    48(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L17
        call    operator delete(void*)
.L17:
        movq    64(%rsp), %rdi
        leaq    80(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L18
        call    operator delete(void*)
.L18:
        movsd   (%rsp), %xmm1
        movq    8(%rsp), %rsi
        leaq    16(%rbx), %rdi
        movl    %r15d, (%rbx)
        movsd   %xmm1, 8(%rbx)
        call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_assign(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
        movq    0(%rbp), %r15
        leaq    80(%rsp), %rax
        movq    8(%rbp), %rbp
        movq    %rax, 64(%rsp)
        movq    %r15, %rax
        addq    %rbp, %rax
        je      .L21
        testq   %r15, %r15
        jne     .L21
        movl    $.LC0, %edi
        call    std::__throw_logic_error(char const*)
.L21:
        cmpq    $15, %rbp
        movq    %rbp, 16(%rsp)
        ja      .L69
        cmpq    $1, %rbp
        je      .L70
        xorl    %edx, %edx
        testq   %rbp, %rbp
        leaq    80(%rsp), %rax
        jne     .L71
.L24:
        movq    %rdx, 72(%rsp)
        movb    $0, (%rax,%rdx)
        leaq    48(%rsp), %rax
        movq    0(%r13), %r15
        movq    8(%r13), %rbp
        movq    %rax, 32(%rsp)
        movq    %r15, %rax
        addq    %rbp, %rax
        je      .L27
        testq   %r15, %r15
        jne     .L27
        movl    $.LC0, %edi
        call    std::__throw_logic_error(char const*)
.L27:
        cmpq    $15, %rbp
        movq    %rbp, 24(%rsp)
        ja      .L72
        cmpq    $1, %rbp
        je      .L73
        xorl    %eax, %eax
        testq   %rbp, %rbp
        leaq    48(%rsp), %rdx
        leaq    24(%rsp), %r13
        jne     .L74
.L30:
        movq    %rax, 40(%rsp)
        leaq    32(%rsp), %rsi
        movb    $0, (%rdx,%rax)
        leaq    64(%rsp), %rdx
        movq    %r13, %rdi
        call    bar::bar(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
        movq    %r13, %rsi
        movq    %r12, %rdi
        call    bar::operator=(bar&&)
        movq    32(%rsp), %rdi
        leaq    48(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L31
        call    operator delete(void*)
.L31:
        movq    64(%rsp), %rdi
        leaq    80(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L16
        call    operator delete(void*)
.L16:
        addq    $104, %rsp
        popq    %rbx
        popq    %rbp
        popq    %r12
        popq    %r13
        popq    %r14
        popq    %r15
        ret
.L69:
        leaq    16(%rsp), %rsi
        leaq    64(%rsp), %rdi
        xorl    %edx, %edx
        call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long)
        movq    16(%rsp), %rdx
        movq    %rax, 64(%rsp)
        movq    %rax, %rdi
        movq    %rdx, 80(%rsp)
.L22:
        movq    %rbp, %rdx
        movq    %r15, %rsi
        call    memcpy
        movq    16(%rsp), %rdx
        movq    64(%rsp), %rax
        jmp     .L24
.L72:
        leaq    24(%rsp), %r13
        leaq    32(%rsp), %rdi
        xorl    %edx, %edx
        movq    %r13, %rsi
        call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long)
        movq    24(%rsp), %rdx
        movq    %rax, 32(%rsp)
        movq    %rax, %rdi
        movq    %rdx, 48(%rsp)
.L28:
        movq    %rbp, %rdx
        movq    %r15, %rsi
        call    memcpy
        movq    24(%rsp), %rax
        movq    32(%rsp), %rdx
        jmp     .L30
.L70:
        movzbl  (%r15), %eax
        movl    $1, %edx
        movb    %al, 80(%rsp)
        leaq    80(%rsp), %rax
        jmp     .L24
.L73:
        movzbl  (%r15), %eax
        leaq    48(%rsp), %rdx
        leaq    24(%rsp), %r13
        movb    %al, 48(%rsp)
        movl    $1, %eax
        jmp     .L30
        movq    %rax, %rbp
        jmp     .L36
        movq    %rax, %rbp
        jmp     .L39
.L74:
        leaq    48(%rsp), %rdi
        leaq    24(%rsp), %r13
        jmp     .L28
.L71:
        movq    %rax, %rdi
        jmp     .L22
.L66:
        movq    %rax, %rbp
        movq    32(%rsp), %rdi
        leaq    48(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L39
        call    operator delete(void*)
.L39:
        movq    64(%rsp), %rdi
        leaq    80(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L36
        call    operator delete(void*)
.L36:
        movq    16(%rbx), %rdi
        cmpq    %rdi, %r14
        je      .L41
        call    operator delete(void*)
.L41:
        movq    %rbp, %rdi
        call    _Unwind_Resume
        jmp     .L66
foo2::foo2(int, double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >):
        pushq   %r12
        pushq   %rbp
        leaq    32(%rdi), %rbp
        pushq   %rbx
        leaq    16(%rdx), %rax
        movq    %rdi, %rbx
        subq    $64, %rsp
        movl    %esi, (%rdi)
        movq    %rbp, 16(%rdi)
        movq    (%rdx), %rsi
        movsd   %xmm0, 8(%rdi)
        cmpq    %rax, %rsi
        je      .L90
        movq    %rsi, 16(%rdi)
        movq    16(%rdx), %rsi
        movq    %rsi, 32(%rdi)
.L77:
        movq    8(%rdx), %rsi
        movq    %rsi, 24(%rbx)
        movq    %rax, (%rdx)
        leaq    48(%rsp), %rax
        movq    $0, 8(%rdx)
        movb    $0, 16(%rdx)
        movq    (%r8), %rdx
        movq    %rax, 32(%rsp)
        leaq    16(%r8), %rax
        cmpq    %rax, %rdx
        je      .L91
        movq    %rdx, 32(%rsp)
        movq    16(%r8), %rdx
        movq    %rdx, 48(%rsp)
.L79:
        movq    8(%r8), %rdx
        movq    %rax, (%r8)
        leaq    16(%rsp), %rax
        movq    $0, 8(%r8)
        movb    $0, 16(%r8)
        movq    %rax, (%rsp)
        leaq    16(%rcx), %rax
        movq    %rdx, 40(%rsp)
        movq    (%rcx), %rdx
        cmpq    %rdx, %rax
        je      .L92
        movq    %rdx, (%rsp)
        movq    16(%rcx), %rdx
        movq    %rdx, 16(%rsp)
.L81:
        movq    8(%rcx), %rdx
        leaq    48(%rbx), %rdi
        movq    %rax, (%rcx)
        movq    $0, 8(%rcx)
        movb    $0, 16(%rcx)
        movq    %rsp, %rsi
        movq    %rdx, 8(%rsp)
        leaq    32(%rsp), %rdx
        call    bar::bar(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
        movq    (%rsp), %rdi
        leaq    16(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L82
        call    operator delete(void*)
.L82:
        movq    32(%rsp), %rdi
        leaq    48(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L75
        call    operator delete(void*)
.L75:
        addq    $64, %rsp
        popq    %rbx
        popq    %rbp
        popq    %r12
        ret
.L90:
        movq    16(%rdx), %rsi
        movq    24(%rdx), %rdi
        movq    %rsi, 32(%rbx)
        movq    %rdi, 40(%rbx)
        jmp     .L77
.L91:
        movq    16(%r8), %rsi
        movq    24(%r8), %rdi
        movq    %rsi, 48(%rsp)
        movq    %rdi, 56(%rsp)
        jmp     .L79
.L92:
        movq    16(%rcx), %rsi
        movq    24(%rcx), %rdi
        movq    %rsi, 16(%rsp)
        movq    %rdi, 24(%rsp)
        jmp     .L81
        movq    %rax, %r12
        movq    (%rsp), %rdi
        leaq    16(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L85
        call    operator delete(void*)
.L85:
        movq    32(%rsp), %rdi
        leaq    48(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L86
        call    operator delete(void*)
.L86:
        movq    16(%rbx), %rdi
        cmpq    %rdi, %rbp
        je      .L87
        call    operator delete(void*)
.L87:
        movq    %r12, %rdi
        call    _Unwind_Resume
.LC4:
        .string "bar"
.LC5:
        .string "foo"
.LC6:
        .string "hello"
main:
        pushq   %rbx
        movl    $.LC4, %esi
        subq    $224, %rsp
        leaq    160(%rsp), %rdi
        call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [clone .isra.16]
        leaq    64(%rsp), %rdi
        movl    $.LC5, %esi
        call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [clone .isra.16]
        leaq    32(%rsp), %rdi
        movl    $.LC6, %esi
        call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [clone .isra.16]
        leaq    160(%rsp), %r8
        leaq    64(%rsp), %rcx
        leaq    32(%rsp), %rdx
        movsd   .LC7(%rip), %xmm0
        leaq    96(%rsp), %rdi
        movl    $45, %esi
        call    foo::foo(int, double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
        movq    32(%rsp), %rdi
        leaq    48(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L94
        call    operator delete(void*)
.L94:
        movq    64(%rsp), %rdi
        leaq    80(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L95
        call    operator delete(void*)
.L95:
        movq    160(%rsp), %rdi
        leaq    176(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L96
        call    operator delete(void*)
.L96:
        leaq    64(%rsp), %rdi
        movl    $.LC4, %esi
        call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [clone .isra.16]
        leaq    32(%rsp), %rdi
        movl    $.LC5, %esi
        call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [clone .isra.16]
        movl    $.LC6, %esi
        movq    %rsp, %rdi
        call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [clone .isra.16]
        leaq    64(%rsp), %r8
        leaq    32(%rsp), %rcx
        leaq    160(%rsp), %rdi
        movsd   .LC7(%rip), %xmm0
        movq    %rsp, %rdx
        movl    $45, %esi
        call    foo2::foo2(int, double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
        movq    (%rsp), %rdi
        leaq    16(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L97
        call    operator delete(void*)
.L97:
        movq    32(%rsp), %rdi
        leaq    48(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L98
        call    operator delete(void*)
.L98:
        movq    64(%rsp), %rdi
        leaq    80(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L99
        call    operator delete(void*)
.L99:
        movq    176(%rsp), %rdi
        leaq    192(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L100
        call    operator delete(void*)
.L100:
        movq    112(%rsp), %rdi
        leaq    128(%rsp), %rax
        cmpq    %rax, %rdi
        je      .L123
        call    operator delete(void*)
.L123:
        addq    $224, %rsp
        xorl    %eax, %eax
        popq    %rbx
        ret
        movq    %rax, %rbx
.L106:
        movq    160(%rsp), %rdi
        leaq    176(%rsp), %rdx
        cmpq    %rdx, %rdi
        je      .L115
.L125:
        call    operator delete(void*)
.L115:
        movq    %rbx, %rdi
        call    _Unwind_Resume
        movq    (%rsp), %rdi
        leaq    16(%rsp), %rdx
        movq    %rax, %rbx
        cmpq    %rdx, %rdi
        je      .L110
        call    operator delete(void*)
.L110:
        movq    32(%rsp), %rdi
        leaq    48(%rsp), %rdx
        cmpq    %rdx, %rdi
        je      .L112
        call    operator delete(void*)
.L112:
        movq    64(%rsp), %rdi
        leaq    80(%rsp), %rdx
        cmpq    %rdx, %rdi
        je      .L114
        call    operator delete(void*)
.L114:
        movq    112(%rsp), %rdi
        leaq    128(%rsp), %rdx
        cmpq    %rdx, %rdi
        jne     .L125
        jmp     .L115
        movq    %rax, %rbx
        jmp     .L110
        movq    %rax, %rbx
        jmp     .L112
        movq    %rax, %rbx
        jmp     .L114
        movq    32(%rsp), %rdi
        leaq    48(%rsp), %rdx
        movq    %rax, %rbx
        cmpq    %rdx, %rdi
        je      .L104
        call    operator delete(void*)
.L104:
        movq    64(%rsp), %rdi
        leaq    80(%rsp), %rdx
        cmpq    %rdx, %rdi
        je      .L106
        call    operator delete(void*)
        jmp     .L106
        movq    %rax, %rbx
        jmp     .L104
.LC7:
        .long   1717986918
        .long   1076389478

Upvotes: 1

Related Questions