簡體   English   中英

GCC在ISR中生成無用的代碼

[英]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 - 臨時寄存器。 普通函數可以隨心所欲地執行任何操作,但ISR必須保留它。
  • r1 - 零寄存器,但可以通過正常功能中的指令寫入。 同樣,必須由ISR保存。

這解釋了為什么保存r0和r1。 GPIOR0顯然只是一個函數可能正在使用的另一個通用寄存器,因此它也需要保存。 如果優化器可以判斷某些寄存器實際上沒有被ISR修改,可能會優化其中的一部分,但優化器可能不那么聰明,或者您可能需要嘗試更高的優化級別。

更多信息請訪問http://gcc.gnu.org/wiki/avr-gcc

有一些關於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序言/結語中執行一些顯然不必要的寄存器保存/恢復:

  • r0r1被保存/恢復,因為編譯器生成或調用的代碼將在上面列出關於它們的假設。 由於它們沒有被GCC的寄存器分配器跟蹤,序言必須確保它們被保存(並且在r1的情況下初始化為0)。

  • r28r29用於保存堆棧指針( 0x3d / SPL0x3e / SPH )。 我猜測(並且我想強調這是猜測)編譯器編寫者認為中斷處理程序交換堆棧可能很常見,這可以確保ISR可以恢復正在使用的堆棧。中斷發生了。 編譯器可以假設這些寄存器不會被被調用的函數改變,因為它們是“調用保存”寄存器。

此外,您應該注意到r0的明顯“額外”推送和彈出是將SREG狀態寄存器保存在堆棧中。 即使在那些pushpop指令之間沒有使用r0 ,請記住r0寄存器是一個暫存寄存器,寄存器分配器不會跟蹤它,所以編譯器不會假設r0在加載SREG后不會改變進去。

另外, 0x3d0x3e的讀取是SPLSPH堆棧指針寄存器,而不是EIMSKGPIOR0寄存器。 有關使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM