Mengapa memori tumpukan dialokasikan ketika tidak digunakan?

14

Perhatikan contoh berikut:

struct vector {
    int  size() const;
    bool empty() const;
};

bool vector::empty() const
{
    return size() == 0;
}

Kode perakitan yang dihasilkan untuk vector::empty(oleh dentang, dengan optimisasi):

push    rax
call    vector::size() const
test    eax, eax
sete    al
pop     rcx
ret

Mengapa ia mengalokasikan ruang stack? Itu tidak digunakan sama sekali. The pushdan popbisa dihilangkan. Membangun MSVC dan gcc yang dioptimalkan juga menggunakan ruang stack untuk fungsi ini (lihat di godbolt ), jadi pasti ada alasannya.

Dr. Gut
sumber
7
Apakah Anda memperhitungkan thisparameter implisit ?
dan04
1
@ Bob: Tidak. Kenapa saya harus? vector::size()tidak didefinisikan dalam contoh untuk mensimulasi bahwa itu tidak diuraikan.
Dr. Gut
1
Jadi, bagaimana mungkin kompiler mengoptimalkan sesuatu yang tidak diketahuinya?
Bob__
1
@ Bob: Saya pikir, mengetahui implementasi vector::size()tidak relevan untuk mengalokasikan atau tidak mengalokasikan frame stack untuk vector::empty(). Di empty()dalamnya hanya disebut, apa pun itu.
Dr. Gut
1
Nah, Anda memanggil fungsi yang mengembalikan sesuatu, Anda perlu ruang untuk itu (jika Anda tidak tahu yang lebih baik).
Bob__

Jawaban:

11

Ini mengalokasikan ruang stack, sehingga tumpukan adalah 16-byte selaras. Dibutuhkan, karena alamat pengirim membutuhkan 8 byte, sehingga diperlukan ruang 8-byte tambahan untuk menjaga stack 16-byte tetap sejajar.

Penjajaran frame tumpukan dapat dikonfigurasi dengan argumen baris perintah untuk beberapa kompiler.

  • MSVC : Dokumentasi mengatakan bahwa stack selalu selaras 16 byte. Tidak ada argumen baris perintah yang dapat mengubah ini. Contoh godbolt menunjukkan bahwa 40 byte dikurangi dari rsppada awal fungsi, yang berarti bahwa sesuatu yang lain juga mempengaruhi ini.
  • Dentang : -mstack-alignmentOpsi menentukan perataan tumpukan. Tampaknya, standarnya adalah 16, meskipun tidak didokumentasikan. Jika Anda mengaturnya ke 8, alokasi tumpukan ( pushdan pop) menghilang dari kode perakitan yang dihasilkan.
  • gcc : -mpreferred-stack-boundaryOpsi menentukan perataan tumpukan. Jika nilai yang diberikan adalah N, itu berarti 2 ^ N byte perataan. Nilai default adalah 4, yang berarti 16 byte. Jika Anda mengaturnya menjadi 3 (yaitu 8 byte), alokasi tumpukan ( subdan adduntuk rsp) menghilang dari kode rakitan yang dihasilkan.

Lihat di godbolt .

geza
sumber
Itu sebabnya c ++ guru, para ahli selalu memperingatkan: menempatkan anggota struct / kelas dalam urutan dari yang terpanjang / terbesar ke terkecil ... hanya dengan cara ini akan lebih efisien
nonock
@geza: Terima kasih. Saya melakukan riset untuk dua kompiler lainnya, dan menulisnya untuk jawaban Anda. Apakah kamu menyukainya?
Dr. Gut
1
@ Dr.Gut: terima kasih, Anda membuat jawabannya jauh lebih baik dan lengkap. Perhatikan, bahwa penjajaran tumpukan biasanya didokumentasikan dalam ABI untuk sistem (misalnya, untuk beberapa sistem, berikut adalah dokumen-dokumennya: github.com/hjl-tools/x86-psABI/wiki/X86-psABI ).
geza
@geza: Terima kasih.
Dr. Gut