Как определение явного деструктора для структуры C ++ влияет на соглашения о вызовах?

Коллега задал мне этот вопрос, заметив любопытное поведение со структурами C ++.

Возьми этот тривиальный код:

struct S {
int i;
#ifdef TEST
~S() {}
#endif
};

void foo (S s) {
(void)s;
}

int main () {
foo(S());
return 0;
}

Я сгенерировал ассемблерный код один раз без явного деструктора:

g++-4.7.2 destructor.cc -S -O0 -o destructor_no.s

и позже включая это:

g++-4.7.2 destructor.cc -DTEST -S -O0 -o destructor_yes.s

Это код [1] для main в destructor_no.s:

main:
pushq   %rbp
movq    %rsp, %rbp
movl    $0, %eax
movl    %eax, %edi
call    _Z3foo1S   // call to foo()
movl    $0, %eax
popq    %rbp
ret

Хотя вместо этого, если деструктор определен явно:

main:
pushq   %rbp
movq    %rsp, %rbp
subq    $16, %rsp
movl    $0, -16(%rbp)
leaq    -16(%rbp), %rax
movq    %rax, %rdi
call    _Z3foo1S   // call to foo()
leaq    -16(%rbp), %rax
movq    %rax, %rdi
call    _ZN1SD1Ev  // call to S::~S()
movl    $0, %eax
leave
ret

Теперь мои знания по сборке немного устарели, но мне кажется, что:

  1. в первом случае структура передается «по значению». Т.е. его содержимое памяти копируется в %edi регистр, который, если я не ошибаюсь, является первым регистром, используемым для передачи аргумента в x86-64 ABI.

  2. во втором случае вместо структуры выделяется в стеке, но foo() функция вызывается с указателем в %rdi,

Почему такая разница?


Заметки:

  • Такое же поведение подтверждается при использовании gcc-4.6.3, или же clang 3.1,

  • Конечно, если оптимизация включена, вызов функции foo() полностью оптимизирован в любом случае.

  • Интересная картина возникает при добавлении дополнительных переменных в struct, если деструктор явно не предоставлен.

До 4 ints (= 16 байт) передаются через регистры аргументов:

pushq   %rbp
movq    %rsp, %rbp
subq    $16, %rsp
movl    $0, -16(%rbp)
movl    $0, -12(%rbp)
movl    $0, -8(%rbp)
movl    $0, -4(%rbp)
movq    -16(%rbp), %rdx
movq    -8(%rbp), %rax
movq    %rdx, %rdi
movq    %rax, %rsi
call    _Z3foo1S

но как только я добавлю пятую int для структуры, аргумент функции, все еще передаваемый «по значению», теперь находится в стеке:

pushq   %rbp
movq    %rsp, %rbp
subq    $56, %rsp
movl    $0, -32(%rbp)
movl    $0, -28(%rbp)
movl    $0, -24(%rbp)
movl    $0, -20(%rbp)
movl    $0, -16(%rbp)
movq    -32(%rbp), %rax
movq    %rax, (%rsp)
movq    -24(%rbp), %rax
movq    %rax, 8(%rsp)
movl    -16(%rbp), %eax
movl    %eax, 16(%rsp)
call    _Z3foo1S
[1] Я удалил некоторые строки, которые я считаю ненужными для целей этого вопроса.

3

Решение

В C ++ 03-говорят, если вы определяете деструктор, ваша структура не является POD-введите больше. Объект варианта без деструктора ведет себя как переменная структуры C (таким образом, он просто передается по значению), в то время как объект с пользовательским определением ведет себя как объект C ++.

3

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

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

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