Question

I want to make sure I am understanding what my code is actually being compiled down to before an exe/library is made from it. I have the following program written in C++98. Which stems from this website 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);  
}

The assembly output, compiled with, g++ -S main.cpp, is below with comments on how I think it should be broken down (this is where I will need some schooling) as well as several questions somewhat clearly marked. Answering the questions in the code below is what I am looking for.

    .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
Was it helpful?

Solution

_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

This is an unoptimized code so compiler does lots of unnecessary stuff like putting values from registers to memory and then back again into registers, but if you follow values around, you will notice there isn't really that much happening. Code gets pointer to storage for object and pointer to vtt entry, it is just deferencing vtt entry and putting found vtable pointer to first 8 bytes of object storage. Vtt being used is actually just a temporary one, used for sub-object construction, as the final one is added by bottom constructor:

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

As for 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

As for linked article, I am unsure what part do you mean. Of what exactly you are unsure? Your code actually doesn't do anything with fields in your classes, and they don't appear in assembly - code is just handling vmt pointers around. What exactly is what you do not understand?

One thing worth noticing is that even though virtual-offsets for sub-class instance are in the vtable, everywhere where object's full type is known those values can be simply hardcoded; same goes for virtual method call and anything that touches vtable for that matter. I am surprised though that unoptimized version still uses hardcoded values.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top