Saya tidak bisa mengerti mengapa kompiler GCC memotong bagian dari kode saya sementara itu benar-benar menjaga yang sama di lingkungan?
Kode C:
#define setb_SYNCO do{(PORTA|= (1<<0));} while(0);
ISR(INT0_vect){
unsigned char i;
i = 10;
while(i>0)i--; // first pause - omitted
setb_SYNCO;
setb_GATE;
i=30;
clrb_SYNCO;
while(i>0)i--; // second pause - preserved
clrb_GATE;
}
Bagian LSS yang sesuai (file assembler, dibuat oleh kompiler):
ISR(INT0_vect){
a4: 1f 92 push r1
a6: 0f 92 push r0
a8: 0f b6 in r0, 0x3f ; 63
aa: 0f 92 push r0
ac: 11 24 eor r1, r1
ae: 8f 93 push r24
unsigned char i;
i = 10;
while(i>0)i--;
setb_SYNCO;
b0: d8 9a sbi 0x1b, 0 ; 27
setb_GATE;
b2: d9 9a sbi 0x1b, 1 ; 27
i=30;
clrb_SYNCO;
b4: d8 98 cbi 0x1b, 0 ; 27
b6: 8e e1 ldi r24, 0x1E ; 30
b8: 81 50 subi r24, 0x01 ; 1
while(i>0)i--;
ba: f1 f7 brne .-4 ; 0xb8 <__vector_1+0x14>
clrb_GATE;
bc: d9 98 cbi 0x1b, 1 ; 27
}
be: 8f 91 pop r24
c0: 0f 90 pop r0
c2: 0f be out 0x3f, r0 ; 63
c4: 0f 90 pop r0
c6: 1f 90 pop r1
c8: 18 95 reti
Saya bisa berasumsi bahwa kompiler mengetahui bahwa kode seperti itu adalah dummy dan memotongnya tetapi mengapa ia mempertahankan kode yang sama di akhir kode?
Apakah ada instruksi penyusun untuk mencegah optimasi tersebut?
Jawaban:
Karena dalam satu komentar Anda menyatakan bahwa "setiap centang CPU layak" Saya sarankan menggunakan beberapa perakitan inline untuk membuat loop penundaan Anda seperti yang Anda inginkan. Solusi ini lebih unggul daripada berbagai
volatile
atau-O0
karena itu memperjelas maksud Anda.Itu harus melakukan trik. Hal yang mudah menguap ada di sana untuk memberitahu kompiler "Saya tahu ini tidak melakukan apa-apa, simpan saja dan percayalah pada saya". Tiga "pernyataan" asm cukup jelas, Anda dapat menggunakan register apa pun alih-alih r24, saya percaya kompiler menyukai register yang lebih rendah sehingga Anda mungkin ingin menggunakan yang tinggi. Setelah yang pertama
:
Anda harus mencantumkan output (baca dan tulis) variabel c, dan tidak ada, setelah yang kedua:
Anda harus mencantumkan input (ronly) variabel c, sekali lagi, tidak ada, dan parameter ketiga adalah daftar register modifikasi yang dipisahkan dengan koma , dalam hal ini r24. Saya tidak yakin apakah Anda harus memasukkan juga register status karenaZERO
flagnya berubah tentu saja, saya tidak memasukkannya.sunting jawaban yang diedit sesuai permintaan OP. Beberapa catatan.
The
"+rm"
sebelum(i)
berarti bahwa Anda membiarkan compiler memutuskan untuk menempatkan saya di m Emory atau di r egister. Itu hal yang baik dalam banyak kasus karena kompiler dapat mengoptimalkan dengan lebih baik jika gratis. Dalam kasus Anda, saya yakin Anda hanya ingin mempertahankan batasan r untuk memaksa saya menjadi register.sumber
c
variabel apa pun alih-alih secara literal yang10
saya sebutkan dalam jawaban asli? Saya mencoba membaca manual GCC tentang penggunaan yang tepat dari konstruksi asm tapi agak dikaburkan bagi saya sekarang. Saya akan sangat menghargai!Anda dapat mencoba membuat loop benar-benar melakukan sesuatu. Saat berdiri, kompiler mengatakan "Lingkaran ini tidak melakukan apa-apa - saya akan menyingkirkannya".
Jadi Anda bisa mencoba konstruk yang sering saya gunakan:
Catatan: tidak semua target untuk kompiler gcc menggunakan sintaks majelis inline yang sama - Anda mungkin perlu menyesuaikannya untuk target Anda.
sumber
Ya, Anda bisa berasumsi itu. Jika Anda mendeklarasikan variabel i sebagai volatile, Anda memberi tahu kompiler untuk tidak mengoptimalkan pada i.
sumber
register unsigned char volatile i __asm__("r1");
mungkin?i
sebagai volatile memecahkan segalanya. Ini dijamin oleh standar C 5.1.2.3. Compiler yang menyesuaikan harus tidak mengoptimalkan loop tersebut jikai
volatile. Untungnya, GCC adalah kompiler yang sesuai. Sayangnya ada banyak calon C kompiler yang tidak sesuai dengan standar, tetapi itu tidak relevan untuk pertanyaan khusus ini. Sama sekali tidak perlu untuk assembler inline.Setelah loop pertama
i
adalah sebuah konstanta. Inisialisasii
dan loop tidak menghasilkan apa-apa selain menghasilkan nilai yang konstan. Tidak ada dalam standar yang menetapkan bahwa loop ini harus dikompilasi apa adanya. Standar juga tidak mengatakan apa pun tentang waktu. Kode yang dikompilasi harus berperilaku seolah-olah ada loop dan itu terjadi. Anda tidak dapat memastikan bahwa pengoptimalan ini dilakukan di bawah standar (waktu tidak dihitung).Loop kedua juga harus dihapus. Saya menganggapnya sebagai bug (atau optimasi yang hilang) yang bukan. Setelah loop
i
adalah nol konstan. Kode harus diganti dengan pengaturani
ke nol.Saya pikir GCC menjaga
i
murni karena alasan Anda melakukan akses port (buram) mungkin mempengaruhii
.Menggunakan
untuk menipu GCC agar percaya bahwa loop melakukan sesuatu.
sumber