[英]GCC generating useless code in ISR
我有一個非常簡單的中斷服務程序(ISR)為atmega328編寫,並使用AVR工作室使用avrgcc(使用-Os)編譯。
ISR (TIMER0_OVF_vect) {
txofcnt++; //count overflows and store in uint16_t
}
如果你注意到生成的程序集(如下),它使用r24,r25來獲取增加易失性uint16_t txofcnt的作業,但它也是push-write-pop r1,r28,r29而沒有讀取它們。 它還有一個額外的r0推/彈,而不會在它們之間使用它。
我不知道為什么r1被推,清除然后最終poped。 但是為什么gcc覺得需要將EIMSK和GPIOR0加載到寄存器中然后不使用它們。 如果你能告訴我GPIOR0的用途是什么,那么數據表說它存在但沒有描述。
00000258 <__vector_16>:
ISR (TIMER0_OVF_vect) {
258: 1f 92 push r1
25a: 0f 92 push r0
25c: 00 90 5f 00 lds r0, 0x005F
260: 0f 92 push r0
262: 11 24 eor r1, r1
264: 8f 93 push r24
266: 9f 93 push r25
268: cf 93 push r28
26a: df 93 push r29
26c: cd b7 in r28, 0x3d ; 61 reads register EIMSK
26e: de b7 in r29, 0x3e ; 62 reads register GPIOR0
txofcnt++;
270: 80 91 0a 01 lds r24, 0x010A
274: 90 91 0b 01 lds r25, 0x010B
278: 01 96 adiw r24, 0x01 ; 1
27a: 90 93 0b 01 sts 0x010B, r25
27e: 80 93 0a 01 sts 0x010A, r24
}
282: df 91 pop r29
284: cf 91 pop r28
286: 9f 91 pop r25
288: 8f 91 pop r24
28a: 0f 90 pop r0
28c: 00 92 5f 00 sts 0x005F, r0
290: 0f 90 pop r0
292: 1f 90 pop r1
294: 18 95 reti
請記住,ISR會中斷當前在CPU上執行的任何代碼。 他們必須小心保存當前CPU狀態(序言)並在退出(結尾)時恢復它,以便中斷的代碼可以正常繼續。
在AVR上,有幾個寄存器不需要在正常的功能序列/結尾中保存/恢復,但需要由ISR保存:
這解釋了為什么保存r0和r1。 GPIOR0顯然只是一個函數可能正在使用的另一個通用寄存器,因此它也需要保存。 如果優化器可以判斷某些寄存器實際上沒有被ISR修改,可能會優化其中的一部分,但優化器可能不那么聰明,或者您可能需要嘗試更高的優化級別。
有一些關於GCC的AVR注冊用法的文檔,請訪問https://gcc.gnu.org/wiki/avr-gcc
與您的問題相關的一些段落:
固定注冊
固定寄存器是GCC寄存器分配器不會分配的寄存器。 寄存器R0和R1是固定的,並在打印出匯編器指令時隱式使用:
R0
用作臨時寄存器,使用后無需恢復。 它必須在中斷服務程序(ISR)序言和結語中保存和恢復。
__tmp_reg__
聯匯編程序中,您可以使用__tmp_reg__
作為臨時寄存器。R1
始終包含零。 在insn期間,內容可能被破壞,例如通過使用R0 / R1作為隱式輸出寄存器的MUL指令。 如果insn破壞了R1,則insn必須在之后將R1恢復為零。 該寄存器必須保存在ISR序言中,然后必須設置為零,因為R1可能包含非零值。 ISR結局恢復了價值。
__zero_reg__
聯匯編程序中,您可以使用__zero_reg__
作為零寄存器。...
呼叫使用的寄存器
呼叫使用或呼叫破壞的通用寄存器(GPR)是可能被函數調用破壞(破壞)的寄存器。
R18-R27,R30,R31
這些GPR被稱為破壞。 普通功能可以在不恢復內容的情況下使用它們。 中斷服務程序(ISR)必須保存和恢復它們使用的每個寄存器。
...
呼叫保存的注冊表
R2-R17,R28,R29
其余的GPR被調用保存,即使用這種寄存器的函數必須恢復其原始內容。 即使寄存器用於傳遞函數參數,這也適用。
接下來我猜測為什么編譯器在ISR序言/結語中執行一些顯然不必要的寄存器保存/恢復:
r0
和r1
被保存/恢復,因為編譯器生成或調用的代碼將在上面列出關於它們的假設。 由於它們沒有被GCC的寄存器分配器跟蹤,序言必須確保它們被保存(並且在r1
的情況下初始化為0)。
r28
和r29
用於保存堆棧指針( 0x3d
/ SPL
和0x3e
/ SPH
)。 我猜測(並且我想強調這是猜測)編譯器編寫者認為中斷處理程序交換堆棧可能很常見,這可以確保ISR可以恢復正在使用的堆棧。中斷發生了。 編譯器可以假設這些寄存器不會被被調用的函數改變,因為它們是“調用保存”寄存器。
此外,您應該注意到r0
的明顯“額外”推送和彈出是將SREG
狀態寄存器保存在堆棧中。 即使在那些push
和pop
指令之間沒有使用r0
,請記住r0
寄存器是一個暫存寄存器,寄存器分配器不會跟蹤它,所以編譯器不會假設r0
在加載SREG
后不會改變進去。
另外, 0x3d
和0x3e
的讀取是SPL
和SPH
堆棧指針寄存器,而不是EIMSK
和GPIOR0
寄存器。 有關使用IN
/ OUT
指令而不是加載或存儲指令時寄存器尋址方式有何不同的詳細信息,請參見參考手冊中的寄存器匯總表(第625頁)的注4。
關於GPIOR0
的獎勵積分:
8.5.1通用I / O寄存器
ATmega48A / PA / 88A / PA / 168A / PA / 328 / P包含三個通用I / O寄存器。 這些寄存器可用於存儲任何信息,它們對於存儲全局變量和狀態標志特別有用。 地址范圍0x00 - 0x1F內的通用I / O寄存器可使用SBI,CBI,SBIS和SBIC指令直接進行位訪問。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.