А не собрать ли нам пример с std::exception?

7 мин на чтение

Задумывались ли вы когда-либо, как выглядит один и тот же код, собранный разными компиляторами?

После статьи /MSVC-exception у меня в голове зародилась мысль, насколько разный код мы получим для msvc и gcc. И вот, пришло время это узнать!

Для этого эксперимента отлично подходит ресурс godbolt.org. Он позволяет переключаться между разными версиями компилятора и анализировать изменения в собранном коде.

Рассмотрим тривиальный пример:

#include "exception"

int main() {
    throw std::exception();
}

gcc

Выберем компилятор gcc 9.2, подождем результата компиляции несколько секунд и вуаля, перед нами дизассемблированный код нашего примера:

std::exception::exception() [base object constructor]:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     edx, OFFSET FLAT:_ZTVSt9exception+16
        mov     rax, QWORD PTR [rbp-8]
        mov     QWORD PTR [rax], rdx
        nop
        pop     rbp
        ret
main:
        push    rbp
        mov     rbp, rsp
        push    rbx
        sub     rsp, 8
        mov     edi, 8
        call    __cxa_allocate_exception
        mov     rbx, rax
        mov     rdi, rbx
        call    std::exception::exception() [complete object constructor]
        mov     edx, OFFSET FLAT:_ZNSt9exceptionD1Ev
        mov     esi, OFFSET FLAT:_ZTISt9exception
        mov     rdi, rbx
        call    __cxa_throw

Добавим оптимизацию -O3 (от уровня отптимизации результат в данном случае не меняется):

main:
        sub     rsp, 8
        mov     edi, 8
        call    __cxa_allocate_exception
        mov     rdi, rax
        mov     QWORD PTR [rax], OFFSET FLAT:_ZTVSt9exception+16
        mov     edx, OFFSET FLAT:_ZNSt9exceptionD1Ev
        mov     esi, OFFSET FLAT:_ZTISt9exception
        call    __cxa_throw

Выглядит просто замечательно.

msvc

Переключаемся на msvc 19.22 x64:

std::exception::`RTTI Base Class Descriptor at (0,-1,0,64)' DD imagerel std::exception `RTTI Type Descriptor' ; std::exception::`RTTI Base Class Descriptor at (0,-1,0,64)'
        DD      00H
        DD      00H
        DD      0ffffffffH
        DD      00H
        DD      040H
        DD      imagerel std::exception::`RTTI Class Hierarchy Descriptor'
std::exception::`RTTI Base Class Array' DD imagerel std::exception::`RTTI Base Class Descriptor at (0,-1,0,64)' ; std::exception::`RTTI Base Class Array'
        ORG $+3
std::exception::`RTTI Class Hierarchy Descriptor' DD 00H                            ; std::exception::`RTTI Class Hierarchy Descriptor'
        DD      00H
        DD      01H
        DD      imagerel std::exception::`RTTI Base Class Array'
const std::exception::`RTTI Complete Object Locator' DD 01H                      ; std::exception::`RTTI Complete Object Locator'
        DD      00H
        DD      00H
        DD      imagerel std::exception `RTTI Type Descriptor'
        DD      imagerel std::exception::`RTTI Class Hierarchy Descriptor'
        DD      imagerel const std::exception::`RTTI Complete Object Locator'
_CT??_R0?AVexception@std@@@8??0exception@std@@QEAA@AEBV01@@Z24 DD 00H
        DD      imagerel std::exception `RTTI Type Descriptor'
        DD      00H
        DD      0ffffffffH
        ORG $+4
        DD      018H
        DD      imagerel std::exception::exception(std::exception const &)
_CTA1 ?? std::AVexception DD 01H
        DD      imagerel _CT??_R0?AVexception@std@@@8??0exception@std@@QEAA@AEBV01@@Z24
_TI1 ?? std::AVexception DD 00H
        DD      imagerel virtual std::exception::~exception(void)
        DD      00H
        DD      imagerel _CTA1 ?? std::AVexception
`string' DB 'Unknown exception', 00H ; `string'
const std::exception::`vftable' DQ FLAT:const std::exception::`RTTI Complete Object Locator'  ; std::exception::`vftable'
        DQ      FLAT:virtual void * std::exception::`vector deleting destructor'(unsigned int)
        DQ      FLAT:virtual char const * std::exception::what(void)const 

$T1 = 32
main    PROC
$LN3:
        sub     rsp, 72                             ; 00000048H
        lea     rcx, QWORD PTR $T1[rsp]
        call    std::exception::exception(void)             ; std::exception::exception
        lea     rdx, OFFSET FLAT:_TI1 ?? std::AVexception
        lea     rcx, QWORD PTR $T1[rsp]
        call    _CxxThrowException
$LN2@main:
        add     rsp, 72                             ; 00000048H
        ret     0
main    ENDP

this$ = 16
std::exception::exception(void) PROC                      ; std::exception::exception, COMDAT
$LN3:
        mov     QWORD PTR [rsp+8], rcx
        push    rdi
        mov     rax, QWORD PTR this$[rsp]
        lea     rcx, OFFSET FLAT:const std::exception::`vftable'
        mov     QWORD PTR [rax], rcx
        mov     rax, QWORD PTR this$[rsp]
        add     rax, 8
        mov     rdi, rax
        xor     eax, eax
        mov     ecx, 16
        rep stosb
        mov     rax, QWORD PTR this$[rsp]
        pop     rdi
        ret     0
std::exception::exception(void) ENDP                      ; std::exception::exception

this$ = 48
_Other$ = 56
std::exception::exception(std::exception const &) PROC             ; std::exception::exception, COMDAT
$LN3:
        mov     QWORD PTR [rsp+16], rdx
        mov     QWORD PTR [rsp+8], rcx
        push    rdi
        sub     rsp, 32                             ; 00000020H
        mov     rax, QWORD PTR this$[rsp]
        lea     rcx, OFFSET FLAT:const std::exception::`vftable'
        mov     QWORD PTR [rax], rcx
        mov     rax, QWORD PTR this$[rsp]
        add     rax, 8
        mov     rdi, rax
        xor     eax, eax
        mov     ecx, 16
        rep stosb
        mov     rax, QWORD PTR this$[rsp]
        add     rax, 8
        mov     rcx, QWORD PTR _Other$[rsp]
        add     rcx, 8
        mov     rdx, rax
        call    __std_exception_copy
        mov     rax, QWORD PTR this$[rsp]
        add     rsp, 32                             ; 00000020H
        pop     rdi
        ret     0
std::exception::exception(std::exception const &) ENDP             ; std::exception::exception

this$ = 48
virtual std::exception::~exception(void) PROC                      ; std::exception::~exception, COMDAT
$LN3:
        mov     QWORD PTR [rsp+8], rcx
        sub     rsp, 40                             ; 00000028H
        mov     rax, QWORD PTR this$[rsp]
        lea     rcx, OFFSET FLAT:const std::exception::`vftable'
        mov     QWORD PTR [rax], rcx
        mov     rax, QWORD PTR this$[rsp]
        add     rax, 8
        mov     rcx, rax
        call    __std_exception_destroy
        add     rsp, 40                             ; 00000028H
        ret     0
virtual std::exception::~exception(void) ENDP                      ; std::exception::~exception

tv69 = 0
this$ = 32
virtual char const * std::exception::what(void)const  PROC                    ; std::exception::what, COMDAT
$LN5:
        mov     QWORD PTR [rsp+8], rcx
        sub     rsp, 24
        mov     rax, QWORD PTR this$[rsp]
        cmp     QWORD PTR [rax+8], 0
        je      SHORT $LN3@what
        mov     rax, QWORD PTR this$[rsp]
        mov     rax, QWORD PTR [rax+8]
        mov     QWORD PTR tv69[rsp], rax
        jmp     SHORT $LN4@what
$LN3@what:
        lea     rax, OFFSET FLAT:`string'
        mov     QWORD PTR tv69[rsp], rax
$LN4@what:
        mov     rax, QWORD PTR tv69[rsp]
        add     rsp, 24
        ret     0
virtual char const * std::exception::what(void)const  ENDP                    ; std::exception::what

this$ = 48
__flags$ = 56
virtual void * std::exception::`scalar deleting destructor'(unsigned int) PROC           ; std::exception::`scalar deleting destructor', COMDAT
$LN4:
        mov     DWORD PTR [rsp+16], edx
        mov     QWORD PTR [rsp+8], rcx
        sub     rsp, 40                             ; 00000028H
        mov     rcx, QWORD PTR this$[rsp]
        call    virtual std::exception::~exception(void)             ; std::exception::~exception
        mov     eax, DWORD PTR __flags$[rsp]
        and     eax, 1
        test    eax, eax
        je      SHORT $LN2@scalar
        mov     edx, 24
        mov     rcx, QWORD PTR this$[rsp]
        call    void operator delete(void *,unsigned __int64)               ; operator delete
$LN2@scalar:
        mov     rax, QWORD PTR this$[rsp]
        add     rsp, 40                             ; 00000028H
        ret     0
virtual void * std::exception::`scalar deleting destructor'(unsigned int) ENDP           ; std::exception::`scalar deleting destructor'

Что за простыня?! Попробуем оптимизировать с опцией /O2:

std::exception::`RTTI Base Class Descriptor at (0,-1,0,64)' DD imagerel std::exception `RTTI Type Descriptor' ; std::exception::`RTTI Base Class Descriptor at (0,-1,0,64)'
        DD      00H
        DD      00H
        DD      0ffffffffH
        DD      00H
        DD      040H
        DD      imagerel std::exception::`RTTI Class Hierarchy Descriptor'
std::exception::`RTTI Base Class Array' DD imagerel std::exception::`RTTI Base Class Descriptor at (0,-1,0,64)' ; std::exception::`RTTI Base Class Array'
        ORG $+3
std::exception::`RTTI Class Hierarchy Descriptor' DD 00H                            ; std::exception::`RTTI Class Hierarchy Descriptor'
        DD      00H
        DD      01H
        DD      imagerel std::exception::`RTTI Base Class Array'
const std::exception::`RTTI Complete Object Locator' DD 01H                      ; std::exception::`RTTI Complete Object Locator'
        DD      00H
        DD      00H
        DD      imagerel std::exception `RTTI Type Descriptor'
        DD      imagerel std::exception::`RTTI Class Hierarchy Descriptor'
        DD      imagerel const std::exception::`RTTI Complete Object Locator'
_CT??_R0?AVexception@std@@@8??0exception@std@@QEAA@AEBV01@@Z24 DD 00H
        DD      imagerel std::exception `RTTI Type Descriptor'
        DD      00H
        DD      0ffffffffH
        ORG $+4
        DD      018H
        DD      imagerel std::exception::exception(std::exception const &)
_CTA1 ?? std::AVexception DD 01H
        DD      imagerel _CT??_R0?AVexception@std@@@8??0exception@std@@QEAA@AEBV01@@Z24
_TI1 ?? std::AVexception DD 00H
        DD      imagerel virtual std::exception::~exception(void)
        DD      00H
        DD      imagerel _CTA1 ?? std::AVexception
`string' DB 'Unknown exception', 00H ; `string'
const std::exception::`vftable' DQ FLAT:const std::exception::`RTTI Complete Object Locator'  ; std::exception::`vftable'
        DQ      FLAT:virtual void * std::exception::`vector deleting destructor'(unsigned int)
        DQ      FLAT:virtual char const * std::exception::what(void)const 

$T1 = 32
main    PROC                                            ; COMDAT
$LN4:
        sub     rsp, 72                             ; 00000048H
        lea     rcx, QWORD PTR $T1[rsp]
        call    std::exception::exception(void)             ; std::exception::exception
        lea     rdx, OFFSET FLAT:_TI1 ?? std::AVexception
        lea     rcx, QWORD PTR $T1[rsp]
        call    _CxxThrowException
        int     3
$LN3@main:
main    ENDP

this$ = 8
std::exception::exception(void) PROC                      ; std::exception::exception, COMDAT
        lea     rax, OFFSET FLAT:const std::exception::`vftable'
        xorps   xmm0, xmm0
        mov     QWORD PTR [rcx], rax
        mov     rax, rcx
        movups  XMMWORD PTR [rcx+8], xmm0
        ret     0
std::exception::exception(void) ENDP                      ; std::exception::exception

this$ = 48
_Other$ = 56
std::exception::exception(std::exception const &) PROC             ; std::exception::exception, COMDAT
$LN5:
        push    rbx
        sub     rsp, 32                             ; 00000020H
        mov     rbx, rcx
        mov     rax, rdx
        lea     rcx, OFFSET FLAT:const std::exception::`vftable'
        xorps   xmm0, xmm0
        lea     rdx, QWORD PTR [rbx+8]
        mov     QWORD PTR [rbx], rcx
        lea     rcx, QWORD PTR [rax+8]
        movups  XMMWORD PTR [rdx], xmm0
        call    __std_exception_copy
        mov     rax, rbx
        add     rsp, 32                             ; 00000020H
        pop     rbx
        ret     0
std::exception::exception(std::exception const &) ENDP             ; std::exception::exception

this$ = 48
__flags$ = 56
virtual void * std::exception::`scalar deleting destructor'(unsigned int) PROC           ; std::exception::`scalar deleting destructor', COMDAT
$LN9:
        mov     QWORD PTR [rsp+8], rbx
        push    rdi
        sub     rsp, 32                             ; 00000020H
        lea     rax, OFFSET FLAT:const std::exception::`vftable'
        mov     rdi, rcx
        mov     QWORD PTR [rcx], rax
        mov     ebx, edx
        add     rcx, 8
        call    __std_exception_destroy
        test    bl, 1
        je      SHORT $LN6@scalar
        mov     edx, 24
        mov     rcx, rdi
        call    void operator delete(void *,unsigned __int64)               ; operator delete
$LN6@scalar:
        mov     rbx, QWORD PTR [rsp+48]
        mov     rax, rdi
        add     rsp, 32                             ; 00000020H
        pop     rdi
        ret     0
virtual void * std::exception::`scalar deleting destructor'(unsigned int) ENDP           ; std::exception::`scalar deleting destructor'

this$ = 8
virtual std::exception::~exception(void) PROC                      ; std::exception::~exception, COMDAT
        lea     rax, OFFSET FLAT:const std::exception::`vftable'
        mov     QWORD PTR [rcx], rax
        add     rcx, 8
        jmp     __std_exception_destroy
virtual std::exception::~exception(void) ENDP                      ; std::exception::~exception

this$ = 8
virtual char const * std::exception::what(void)const  PROC                    ; std::exception::what, COMDAT
        mov     rdx, QWORD PTR [rcx+8]
        lea     rax, OFFSET FLAT:`string'
        test    rdx, rdx
        cmovne  rax, rdx
        ret     0
virtual char const * std::exception::what(void)const  ENDP                    ; std::exception::what

Это же просто ужасно, столько команд ради такого скромного функционала…

clang

Для полноты картины добавим x86-x64 clang 9.0:

main:                                   # @main
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     dword ptr [rbp - 4], 0
        mov     edi, 8
        call    __cxa_allocate_exception
        mov     rcx, rax
        mov     rdi, rcx
        mov     qword ptr [rbp - 16], rax # 8-byte Spill
        call    std::exception::exception() [base object constructor]
        movabs  rax, offset typeinfo for std::exception
        movabs  rcx, offset std::exception::~exception() [complete object destructor]
        mov     rdi, qword ptr [rbp - 16] # 8-byte Reload
        mov     rsi, rax
        mov     rdx, rcx
        call    __cxa_throw
std::exception::exception() [base object constructor]:                    # @std::exception::exception() [base object constructor]
        push    rbp
        mov     rbp, rsp
        movabs  rax, offset vtable for std::exception
        add     rax, 16
        mov     qword ptr [rbp - 8], rdi
        mov     rcx, qword ptr [rbp - 8]
        mov     qword ptr [rcx], rax
        pop     rbp
        ret

Добавим оптимизацию -O2:

main:                                   # @main
        push    rax
        mov     edi, 8
        call    __cxa_allocate_exception
        mov     qword ptr [rax], offset vtable for std::exception+16
        mov     esi, offset typeinfo for std::exception
        mov     edx, offset std::exception::~exception() [complete object destructor]
        mov     rdi, rax
        call    __cxa_throw

Очень приятный результат, особенно после msvc.

Дата изменения: