Mengapa kompiler tidak menggunakan LSR secara langsung

10

Hai Saya sudah mengerjakan proyek menggunakan Arduino Uno (jadi ATmega328p) di mana waktunya cukup penting dan jadi saya ingin melihat ke dalam instruksi mana kompiler mengubah kode saya. Dan di sana saya memiliki uint8_tyang saya menggeser satu bit ke kanan pada setiap iterasi menggunakan data >>= 1dan tampaknya kompiler menerjemahkan ini ke dalam 5 instruksi ( dataada di r24):

mov     r18, r24
ldi     r19, 0x00
asr     r19
ror     r18
mov     r24, r18

Tetapi jika saya melihat dokumentasi set instruksi saya melihat instruksi yang melakukan hal ini: lsr r24

Apakah saya mengabaikan sesuatu atau mengapa kompiler tidak menggunakan ini juga? Register r18dan r19tidak digunakan di tempat lain.

Saya menggunakan Ardunio tetapi jika saya benar itu hanya menggunakan avr-gcckompiler normal . Ini adalah kode (dipangkas) yang menghasilkan urutan:

ISR(PCINT0_vect) {
    uint8_t data = 0;
    for (uint8_t i = 8; i > 0; --i) {
//        asm volatile ("lsr %0": "+w" (data));
        data >>= 1;
        if (PINB & (1 << PB0))
            data |= 0x80;
    }
    host_data = data;
}

Sejauh yang saya bisa lihat, Ardunino IDE menggunakan kompiler gcc AVR yang disediakan oleh sistem yang merupakan versi 6.2.0-1.fc24. Keduanya dipasang melalui palungan paket sehingga harus diperbarui.

xZise
sumber
1
Perakitan tampaknya tidak sesuai dengan kode C.
Eugene Sh.
Yah saya mengompilasinya menggunakan Ardunio IDE dan kemudian digunakan avr-objdumppada file elf ... Apa yang tampaknya tidak sesuai?
xZise
1
@Eugene Sh .: Ini tidak sesuai dengan kode C. Ini sesuai dengan garisdata >>= 1;
Curd
1
Ini adalah salah satu kasus di mana "gunakan shift alih-alih pembagian" adalah saran yang salah. Jika Anda melakukan / = 2 sebagai gantinya kompiler akan menghasilkan lsr r24; (tip: coba penjelajah gcc untuk bermain-main dengan pembuatan kode asm)
PlasmaHH
Kompiler apa ? Prosesor apa? Benar-benar harus jelas ini adalah informasi yang diperlukan agar pertanyaan masuk akal.
Olin Lathrop

Jawaban:

18

Menurut spesifikasi bahasa C, nilai apa pun yang ukurannya kurang dari ukuran int(tergantung pada kompiler tertentu; dalam kasus Anda intlebar 16-bit) yang terlibat dalam operasi apa pun (dalam kasus Anda >>) dikirim ke intsebelum operasi.
Perilaku kompiler ini disebut promosi integer .

Dan itulah yang dilakukan oleh kompiler:

  • r19 = 0 adalah MSByte dari nilai yang dipromosikan integer data.
  • (r19, r18) mewakili total nilai integer yang dipromosikan datayang kemudian bergeser ke kanan sedikit demi sedikit oleh asr r19dan ror 18.
  • Hasilnya kemudian secara implisit dilemparkan kembali ke Anda uint8_tvariabel data:
    mov r24, r18, yaitu MSByte di R19 dibuang.

Sunting:
Tentu saja penyusun dapat mengoptimalkan kode.
Mencoba mereproduksi masalah saya menemukan bahwa setidaknya dengan avr-gcc versi 4.9.2 masalah tidak terjadi. Ini menciptakan kode yang sangat efisien, yaitu C-line data >>= 1;dikompilasi menjadi hanya satu lsr r24instruksi tunggal . Jadi mungkin Anda menggunakan versi kompiler yang sangat lama.

Dadih
sumber
2
Ini bukan pemborosan total karena kadang-kadang Anda memerlukan kode yang tidak dioptimalkan untuk debugging pada tingkat assembler. Maka Anda sangat senang jika Anda memiliki kode yang tidak dioptimalkan.
Curd
3
Jika saya ingat dengan benar -mint8 adalah flag untuk membuat integer 8-bit. Namun ini memiliki banyak efek samping yang tidak diinginkan. Maaf, tidak bisa mengingat mereka sekarang, tetapi saya tidak pernah menggunakan bendera karena mereka. Saya menghabiskan banyak waktu membandingkan avr-gcc dengan kompiler komersial bertahun-tahun yang lalu.
Jon
1
Oh itu benar, standar C membutuhkan integer setidaknya 16-bit, jadi menggunakan -mint8 memecah semua pustaka.
Jon
9
Nigel Jones mengatakan dalam "Kode C Efisien untuk Mikrokontroler 8-bit" sesuatu seperti: "... Aturan promosi integer C mungkin merupakan kejahatan paling keji yang dilakukan terhadap kita yang bekerja di dunia 8-bit" ...
Dirceu Rodrigues Jr
1
@Jonas Wielicki: solusi terbaik untuk masalah ini adalah menggunakan kompiler yang lebih baik. Misalnya dengan avr-gcc versi 4.9.2 Saya tidak dapat mereproduksi masalah: Untuk baris kode C d >>= 1;saya hanya mendapatkan satu lsr r24instruksi tunggal . Mungkin xZise menggunakan versi kompiler yang sangat lama.
Curd