GCC creates useless code in ISR

I have a very simple interrupt service routine (ISR) written for atmega328 and compiled using avrgcc (using -Os) using the AVR studio.

ISR (TIMER0_OVF_vect) { txofcnt++; //count overflows and store in uint16_t } 

If you notice the assembly generated (see below), it uses r24, r25 to get the job increasing volatile uint16_t txofcnt, but also push-write-pop-r-r1, r28, r29 without reading them. It also has an extra push / pop of r0, without using it between them.

I do not understand why r1 is pushed, cleaned, and then unloaded. But also why gcc feels the need to load EIMSK and GPIOR0 into registers and then not use them. Bonus points, if you can tell me what GPIOR0 is suitable for, the data table indicates that it exists, but does not have a description.

 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 
+6
source share
2 answers

There is some documentation on using the GCC register for AVR at https://gcc.gnu.org/wiki/avr-gcc

Some passages related to your question:

Fixed registers

Fixed registers are registers that will not be allocated by GCC. register allocator. Registers R0 and R1 are fixed and are used implicitly when printing assembler instructions:

  • R0

    It is used as a scratch register that does not need to be restored after its use. It must be saved and restored to the interrupt service routine (ISR) prologue and epilogue. In built-in assembler, you can use __tmp_reg__ to register zero.

  • R 1

    always contains zero. During insn, the contents may be destroyed, for example. using the MUL instruction, which uses R0 / R1 as an implicit output register. If insn destroys R1, insn must restore R1 to zero after. This register must be stored in ISR prologs and must then be set to zero, because R1 may contain values ​​other than zero. The epilogue ISR restores value. In inline assembler, you can use __zero_reg__ for case-insensitive.

    ...

Registers used during registration

General purpose registers (GPR) used when calling or calling registers that can be destroyed (knocked down) by calling a function.

  • R18-R27, R30, R31

    These GPR calls are dropped. A regular function can use them without restoring content. Interrupt Service Procedures (ISRs) must save and restore each register it registers.

    ...

Registers with a record

  • R2-R17, R28, R29

    The remaining GPR calls are saved, i.e. a function using such registers should restore the original contents. This applies even if case is used to pass an argument to a function.

The following is my assumption about why the compiler performs some obviously unnecessary save / restore registers in the ISR prolog / epilogue:

  • r0 and r1 are saved / restored because the code that the compiler generates or calls will make assumptions about them above. Since they are not monitored by the GCC register allocator, the prolog must ensure that they are saved (and in the case of r1 initialized to 0).

  • r28 and r29 are used to store the stack pointer ( 0x3d / SPL and 0x3e / SPH ). I assume (and I want to emphasize that this assumption) that the authors of the compiler assume that the interrupt handler may be common to swap stacks, and this ensures that the ISR will be able to restore this stack, which was used when the interrupt occurred. The compiler may assume that these registers will not be changed by the called functions, as they are “persistent calls”.

In addition, you should note that, apparently, the “extra” push and pop r0 should keep the SREG status SREG on the stack. Although r0 not used between these push and pop instructions, remember that r0 is a zero register that is not tracked by the register allocator, so the compiler does not assume that r0 will not be changed after it SREG into it.

As a side note, the 0x3d and 0x3e are SPL and SPH stack pointer registers, not EIMSK and GPIOR0 . See Note 4 to the registry pivot table on page 625 in the reference guide here for a detailed description of how register addressing differs when using IN / OUT instead of a load or store instruction.

And for bonus points regarding GPIOR0 :

8.5.1 General Purpose I / O Registers

The ATmega48A / PA / 88A / PA / 168A / PA / 328 / P contains three main purposes of the I / O Registers. These registers can be used to store any information, and they are especially useful for storing global variables and status flags. General purpose I / O registers in the address range 0x00 - 0x1F are directly accessible for bits using SBI, CBI, SBIS and SBIC.

+3
source

Keep in mind that ISRs interrupt any code currently running on the CPU. They must be careful to preserve the current state of the processor upon entry (prolog) and restore it upon exit (epilogue) so that interrupted code can continue normally.

AVR has a couple of registers that do not need to be saved / restored in ordinary prolog functions / epilogues, but they must be saved using ISR:

  • r0 is a zero register. Normal functions can do whatever they want with them, but ISRs must keep it.
  • r1 is a zero register, but it can be written with instructions in ordinary functions. Again, the ISR should be saved.

This explains why r0 and r1 are stored. GPIOR0 is apparently another universal register that a function can use, so it must also be preserved. Perhaps some of them can be optimized if the optimizer can say that some registers are not actually modified by the ISR, but the optimizer may not be so smart, or you may need a higher level of optimization.

Additional information is available at http://gcc.gnu.org/wiki/avr-gcc

+3
source

Source: https://habr.com/ru/post/970057/


All Articles