g ++ Сборка вывода простой программы с использованием виртуального наследования

Я хочу убедиться, что я понимаю, что мой код на самом деле компилируется до того, как из него будет сделана exe / библиотека. У меня есть следующая программа, написанная на C ++ 98.
Что происходит от этого сайта http://www.phpcompiler.org/articles/virtualinheritance.html
.

#include <stdio.h>

class top
{
public:
int t;
};

class left : virtual public top
{
public:
int l;
};

class right : virtual public top
{
public:
int r;
};

class bottom : public left, public right
{
public:
int b;
};

int main()
{
bottom *b = new bottom();
b->l = 5;
left *l = b;
printf("%d\n", l->l);
}

Вывод сборки, скомпилированный с g++ -S main.cppНиже приводятся комментарии о том, как, по моему мнению, это должно быть разбито (именно здесь мне понадобится обучение), а также несколько вопросов, несколько четко обозначенных. Отвечая на вопросы в коде ниже, это то, что я ищу.

    .file   "main.cpp".section    .text._ZN3topC2Ev,"axG",@progbits,_ZN3topC5Ev,comdat
.align 2
.weak   _ZN3topC2Ev
.type   _ZN3topC2Ev, @function
_ZN3topC2Ev:
.LFB3:
.cfi_startproc                                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pushq   %rbp                                          ; LFB3 Associated with the address for the class top constructor
.cfi_def_cfa_offset 16                                ;
.cfi_offset 6, -16                                    ; %rdi, -8(%rbp) pushes 8 bytes (64 bits for t).
movq    %rsp, %rbp                                    ; onto the stack
.cfi_def_cfa_register 6
movq    %rdi, -8(%rbp)
popq    %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE3:
.size   _ZN3topC2Ev, .-_ZN3topC2Ev
.weak   _ZN3topC1Ev
.set    _ZN3topC1Ev,_ZN3topC2Ev
.section    .text._ZN4leftC2Ev,"axG",@progbits,_ZN4leftC2Ev,comdat
.align 2
.weak   _ZN4leftC2Ev
.type   _ZN4leftC2Ev, @function
_ZN4leftC2Ev:
.LFB6:
.cfi_startproc                                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pushq   %rbp                                          ; LFB6 Associated with the adress for the class left constructor
.cfi_def_cfa_offset 16                                ;
.cfi_offset 6, -16                                    ; %rdi, -8(%rbp) pushes 8 bytes (64 bits for l).
movq    %rsp, %rbp                                    ; onto the stack
.cfi_def_cfa_register 6                               ;
movq    %rdi, -8(%rbp)                                ; %rdi, -16(%rbp) pushes 8 more bytes (64 bits for t).
movq    %rsi, -16(%rbp)                               ;
movq    -16(%rbp), %rax                               ; What does the rest of this do?
movq    (%rax), %rdx
movq    -8(%rbp), %rax
movq    %rdx, (%rax)
popq    %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE6:
.size   _ZN4leftC2Ev, .-_ZN4leftC2Ev
.section    .text._ZN5rightC2Ev,"axG",@progbits,_ZN5rightC2Ev,comdat
.align 2
.weak   _ZN5rightC2Ev
.type   _ZN5rightC2Ev, @function
_ZN5rightC2Ev:
.LFB9:
.cfi_startproc                                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pushq   %rbp                                          ; LFB9 Associated with the adress for the class left constructor
.cfi_def_cfa_offset 16                                ;
.cfi_offset 6, -16                                    ; %rdi, -8(%rbp) pushes 8 bytes (64 bits for r).
movq    %rsp, %rbp                                    ; onto the stack
.cfi_def_cfa_register 6                               ;
movq    %rdi, -8(%rbp)                                ; %rdi, -16(%rbp) pushes 8 more bytes (64 bits for t).
movq    %rsi, -16(%rbp)                               ;
movq    -16(%rbp), %rax                               ; What does the rest of this do?
movq    (%rax), %rdx
movq    -8(%rbp), %rax
movq    %rdx, (%rax)
popq    %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE9:
.size   _ZN5rightC2Ev, .-_ZN5rightC2Ev
.section    .text._ZN6bottomC1Ev,"axG",@progbits,_ZN6bottomC1Ev,comdat
.align 2
.weak   _ZN6bottomC1Ev
.type   _ZN6bottomC1Ev, @function
_ZN6bottomC1Ev:
.LFB12:
.cfi_startproc                                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pushq   %rbp                                          ; LFB12 Associated with the adress for the class left constructor
.cfi_def_cfa_offset 16                                ;
.cfi_offset 6, -16                                    ; %rdi, -8(%rbp) pushes 8 bytes (64 bits for b).
movq    %rsp, %rbp                                    ; onto the stack
.cfi_def_cfa_register 6                               ;
subq    $16, %rsp                                     ; Construct all the base objects placing t into b only once?
movq    %rdi, -8(%rbp)                                ;
movq    -8(%rbp), %rax
addq    $32, %rax
movq    %rax, %rdi
call    _ZN3topC2Ev
movl    $_ZTT6bottom+8, %edx
movq    -8(%rbp), %rax
movq    %rdx, %rsi
movq    %rax, %rdi
call    _ZN4leftC2Ev
movl    $_ZTT6bottom+16, %eax
movq    -8(%rbp), %rdx
addq    $16, %rdx
movq    %rax, %rsi
movq    %rdx, %rdi
call    _ZN5rightC2Ev
movl    $_ZTV6bottom+24, %edx
movq    -8(%rbp), %rax
movq    %rdx, (%rax)
movl    $_ZTV6bottom+48, %edx
movq    -8(%rbp), %rax
movq    %rdx, 16(%rax)
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE12:
.size   _ZN6bottomC1Ev, .-_ZN6bottomC1Ev
.section    .rodata
.LC0:
.string "%d\n".text
.globl  main
.type   main, @function
main:
.LFB0:
.cfi_startproc                                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pushq   %rbp                                          ; Store off the base pointer
.cfi_def_cfa_offset 16                                ; Debug code to trace where stack pointer is?
.cfi_offset 6, -16                                    ;
movq    %rsp, %rbp                                    ; Move where stack pointer is to base pointer
.cfi_def_cfa_register 6                               ;
pushq   %rbx                                          ; Store off what might have been in rbx
subq    $24, %rsp                                     ; Push argc onto stack
.cfi_offset 3, -24                                    ;
movl    $40, %edi                                     ; Push argv onto stack
call    _Znwm                                         ; Call new
movq    %rax, %rbx                                    ; Create room for b %rax contains address of memory
movq    $0, (%rbx)                                    ; location where new returned?
movq    $0, 8(%rbx)                                   ;
movq    $0, 16(%rbx)                                  ;
movq    $0, 24(%rbx)                                  ;
movq    $0, 32(%rbx)                                  ;
movq    %rbx, %rdi                                    ; Move that data into dynamic memory?
call    _ZN6bottomC1Ev                                ; Call the construtor of the object
movq    %rbx, -32(%rbp)                               ;
movq    -32(%rbp), %rax                               ; Can someone explain how this code relates to
movl    $5, 8(%rax)                                   ; the explaination at:
movq    -32(%rbp), %rax                               ; http://www.phpcompiler.org/articles/virtualinheritance.html?
movq    %rax, -24(%rbp)
movq    -24(%rbp), %rax
movl    8(%rax), %eax
movl    %eax, %esi
movl    $.LC0, %edi
movl    $0, %eax
call    printf
movl    $0, %eax
addq    $24, %rsp
popq    %rbx
popq    %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size   main, .-main
.weak   _ZTV6bottom
.section    .rodata._ZTV6bottom,"aG",@progbits,_ZTV6bottom,comdat
.align 32
.type   _ZTV6bottom, @object
.size   _ZTV6bottom, 48
_ZTV6bottom:
.quad   32
.quad   0
.quad   _ZTI6bottom
.quad   16
.quad   -16
.quad   _ZTI6bottom
.weak   _ZTT6bottom
.section    .rodata._ZTT6bottom,"aG",@progbits,_ZTV6bottom,comdat
.align 32
.type   _ZTT6bottom, @object
.size   _ZTT6bottom, 32
_ZTT6bottom:
.quad   _ZTV6bottom+24
.quad   _ZTC6bottom0_4left+24
.quad   _ZTC6bottom16_5right+24
.quad   _ZTV6bottom+48
.weak   _ZTC6bottom0_4left
.section    .rodata._ZTC6bottom0_4left,"aG",@progbits,_ZTV6bottom,comdat
.align 16
.type   _ZTC6bottom0_4left, @object
.size   _ZTC6bottom0_4left, 24
_ZTC6bottom0_4left:
.quad   32
.quad   0
.quad   _ZTI4left
.weak   _ZTC6bottom16_5right
.section    .rodata._ZTC6bottom16_5right,"aG",@progbits,_ZTV6bottom,comdat
.align 16
.type   _ZTC6bottom16_5right, @object
.size   _ZTC6bottom16_5right, 24
_ZTC6bottom16_5right:
.quad   16
.quad   0
.quad   _ZTI5right
.weak   _ZTS6bottom
.section    .rodata._ZTS6bottom,"aG",@progbits,_ZTS6bottom,comdat
.type   _ZTS6bottom, @object
.size   _ZTS6bottom, 8
_ZTS6bottom:
.string "6bottom".weak   _ZTI6bottom
.section    .rodata._ZTI6bottom,"aG",@progbits,_ZTI6bottom,comdat
.align 32
.type   _ZTI6bottom, @object
.size   _ZTI6bottom, 56
_ZTI6bottom:
.quad   _ZTVN10__cxxabiv121__vmi_class_type_infoE+16
.quad   _ZTS6bottom
.long   2
.long   2
.quad   _ZTI4left
.quad   2
.quad   _ZTI5right
.quad   4098
.weak   _ZTS5right
.section    .rodata._ZTS5right,"aG",@progbits,_ZTS5right,comdat
.type   _ZTS5right, @object
.size   _ZTS5right, 7
_ZTS5right:
.string "5right".weak   _ZTI5right
.section    .rodata._ZTI5right,"aG",@progbits,_ZTI5right,comdat
.align 32
.type   _ZTI5right, @object
.size   _ZTI5right, 40
_ZTI5right:
.quad   _ZTVN10__cxxabiv121__vmi_class_type_infoE+16
.quad   _ZTS5right
.long   0
.long   1
.quad   _ZTI3top
.quad   -6141
.weak   _ZTS4left
.section    .rodata._ZTS4left,"aG",@progbits,_ZTS4left,comdat
.type   _ZTS4left, @object
.size   _ZTS4left, 6
_ZTS4left:
.string "4left".weak   _ZTI4left
.section    .rodata._ZTI4left,"aG",@progbits,_ZTI4left,comdat
.align 32
.type   _ZTI4left, @object
.size   _ZTI4left, 40
_ZTI4left:
.quad   _ZTVN10__cxxabiv121__vmi_class_type_infoE+16
.quad   _ZTS4left
.long   0
.long   1
.quad   _ZTI3top
.quad   -6141
.weak   _ZTS3top
.section    .rodata._ZTS3top,"aG",@progbits,_ZTS3top,comdat
.type   _ZTS3top, @object
.size   _ZTS3top, 5
_ZTS3top:
.string "3top".weak   _ZTI3top
.section    .rodata._ZTI3top,"aG",@progbits,_ZTI3top,comdat
.align 16
.type   _ZTI3top, @object
.size   _ZTI3top, 16
_ZTI3top:
.quad   _ZTVN10__cxxabiv117__class_type_infoE+16
.quad   _ZTS3top
.ident  "GCC: (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3".section    .note.GNU-stack,"",@progbits

1

Решение

_ZN4leftC2Ev:
.LFB6:
.cfi_startproc                                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pushq   %rbp                                          ; LFB6 Associated with the adress for the class left constructor
.cfi_def_cfa_offset 16                                ;
.cfi_offset 6, -16                                    ; %rdi, -8(%rbp) - his doesn't initialize anything - it is just pushing this value on local stack, in optimized version it will probably dissapear.
movq    %rsp, %rbp                                    ; %rsi, -16(%rbp) - just as above;
.cfi_def_cfa_register 6                               ; %rsi - pointer to virtual table table (not a mistake it's vtt, not vt) for left-in-bottom
movq    %rdi, -8(%rbp)                                ;  %rdi - pointer to left instance
movq    %rsi, -16(%rbp)                               ;
movq    -16(%rbp), %rax                               ; What does the rest of this do?
movq    (%rax), %rdx                                  ;  It is just copying vtable address form vtt to first eight bytes of actual object.
movq    -8(%rbp), %rax
movq    %rdx, (%rax)
popq    %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc

Это неоптимизированный код, поэтому компилятор делает много ненужных вещей, например, помещает значения из регистров в память, а затем обратно в регистры, но если вы будете следовать значениям вокруг, вы заметите, что на самом деле ничего особенного не происходит. Код получает указатель на хранилище для объекта и указатель на запись vtt, он просто защищает запись vtt и помещает найденный указатель vtable в первые 8 байтов хранилища объекта. Используемый Vtt на самом деле является временным, используется для создания подобъекта, так как последний добавляется bottom конструктор:

    movl    $_ZTV6bottom+24, %edx
movq    -8(%rbp), %rax
movq    %rdx, (%rax)
movl    $_ZTV6bottom+48, %edx
movq    -8(%rbp), %rax
movq    %rdx, 16(%rax)

Что касается main:

main:
.LFB0:
.cfi_startproc                                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pushq   %rbp                                          ; Store off the base pointer
.cfi_def_cfa_offset 16                                ; Debug code to trace where stack pointer is?
.cfi_offset 6, -16                                    ;
movq    %rsp, %rbp                                    ; Move where stack pointer is to base pointer
.cfi_def_cfa_register 6                               ;
pushq   %rbx                                          ; Store off what might have been in rbx
subq    $24, %rsp                                     ; Push argc onto stack
.cfi_offset 3, -24                                    ;
movl    $40, %edi                                     ; Push argv onto stack
call    _Znwm                                         ; Call new
movq    %rax, %rbx                                    ; Create room for b %rax contains address of memory
movq    $0, (%rbx)                                    ; location where new returned?
movq    $0, 8(%rbx)                                   ; A: Yes - eax contains address of returned buffer
movq    $0, 16(%rbx)                                  ;    It is being zeroed to 5*8 = 48 bytes
movq    $0, 24(%rbx)                                  ;
movq    $0, 32(%rbx)                                  ;
movq    %rbx, %rdi                                    ; Move that data into dynamic memory? -hmmm, what? Just moving pointr to it into rdi, where bottom constructor expects it
; it is still where it was - in dynamic memory from new (_Znwm), jut it's pointer changed register ;)
call    _ZN6bottomC1Ev                                ; Call the construtor of the object
movq    %rbx, -32(%rbp)                               ;
movq    -32(%rbp), %rax                               ; Here isn't happening much - classes bottom and left are sure to start at the same address, so compiler doesn't need to chack for anything,
movl    $5, 8(%rax)                                   ; just use offset to addres fields, and copy the pointer without modification to do the castting from bottom to left.
movq    -32(%rbp), %rax                               ;
movq    %rax, -24(%rbp)
movq    -24(%rbp), %rax
movl    8(%rax), %eax
movl    %eax, %esi
movl    $.LC0, %edi
movl    $0, %eax
call    printf
movl    $0, %eax
addq    $24, %rsp
popq    %rbx
popq    %rbp
.cfi_def_cfa 7, 8
ret

Что касается связанной статьи, я не уверен, какую часть вы имеете в виду. В чем именно вы не уверены? Ваш код фактически ничего не делает с полями в ваших классах, и они не появляются в сборке — код просто обрабатывает указатели vmt. Что именно то, что вы не понимаете?

Стоит отметить, что хотя виртуальные смещения для экземпляра подкласса находятся в vtable, везде, где известен полный тип объекта, эти значения могут быть просто жестко закодированы; То же самое касается виртуального вызова метода и всего, что касается vtable. Я удивлен тем, что эта неоптимизированная версия все еще использует жестко закодированные значения.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]