Forum: Compiler & IDEs warum soviel Pop ud Push ?


von Michael (Gast)


Lesenswert?

Hallo,

ich habe mir mal das Assembler Progamm angeschaut, welches der GCC
erstellt hat.
Nun wundert es mich, warum soviele Register auf dem Stack gerettet
werden, obwohl sie in der Routine gar nicht verändert werden.

Erstmal das C und Assemblerprogramm vom Interruptaufruf:

#C

SIGNAL (SIG_OVERFLOW0)
  {
    systimer_int();
  }


# ASM

SIGNAL (SIG_OVERFLOW0)
        {
 3ca:   1f 92           push    r1
 3cc:   0f 92           push    r0
 3ce:   0f b6           in      r0, 0x3f        ; 63
 3d0:   0f 92           push    r0
 3d2:   11 24           eor     r1, r1
 3d4:   2f 93           push    r18
 3d6:   3f 93           push    r19
 3d8:   4f 93           push    r20
 3da:   5f 93           push    r21
 3dc:   6f 93           push    r22
 3de:   7f 93           push    r23
 3e0:   8f 93           push    r24
 3e2:   9f 93           push    r25
 3e4:   af 93           push    r26
 3e6:   bf 93           push    r27
 3e8:   ef 93           push    r30
 3ea:   ff 93           push    r31
                systimer_int();
 3ec:   3f de           rcall   .-898           ; 0x6c
        }
 3ee:   ff 91           pop     r31
 3f0:   ef 91           pop     r30
 3f2:   bf 91           pop     r27
 3f4:   af 91           pop     r26
 3f6:   9f 91           pop     r25
 3f8:   8f 91           pop     r24
 3fa:   7f 91           pop     r23
 3fc:   6f 91           pop     r22
 3fe:   5f 91           pop     r21
 400:   4f 91           pop     r20
 402:   3f 91           pop     r19
 404:   2f 91           pop     r18
 406:   0f 90           pop     r0
 408:   0f be           out     0x3f, r0        ; 63
 40a:   0f 90           pop     r0
 40c:   1f 90           pop     r1
 40e:   18 95           reti


und nun das Interruptprogramm:

#C

void systimer_int(void)
  {
    extern volatile unsigned short restzeit;
    static char systimer_time = 0;
    outp (5, TCNT0);
    systimer_time++;
    restzeit--;
  }


# ASM


0000006c <systimer_int>:


void systimer_int(void)
        {
                extern volatile unsigned short restzeit;
                static char systimer_time = 0;
                outp (5, TCNT0);
  6c:   85 e0           ldi     r24, 0x05       ; 5
  6e:   82 bf           out     0x32, r24       ; 50
                systimer_time++;
  70:   80 91 65 00     lds     r24, 0x0065
  74:   8f 5f           subi    r24, 0xFF       ; 255
  76:   80 93 65 00     sts     0x0065, r24
                restzeit--;
  7a:   80 91 61 00     lds     r24, 0x0061
  7e:   90 91 62 00     lds     r25, 0x0062
  82:   01 97           sbiw    r24, 0x01       ; 1
  84:   90 93 62 00     sts     0x0062, r25
  88:   80 93 61 00     sts     0x0061, r24
        }
  8c:   08 95           ret

Warum ist das so und kann man das irgendwie abstellen ?

Jogibär

von Matthias (Gast)


Lesenswert?

Hi

weil der GCC nicht weiß welche Register in systimer_int() verändert
werden pusht er alle Arbeitsregister auf den Steck. Schreibe den Code
aus systimer_int() direkt in die ISR und die push's werden
verschwinden.

Matthias

von Michael (Gast)


Lesenswert?

Hallo,

das hatte ich schon ausprobiert, und dann wird der Kode auch
kleiner.
Ich dachte immer, der GCC versucht soetwas zu optimieren.
Allerdings sieht ja der GCC im Assemblerprogramm welche Register er
wirklich benutzt, und müßte dann die unbenutzten Register vom
poppen ausnehmen.

Ich denke, ich werde alle Interrupteinsprünge in eine extra Datei
aufnehmen, dann herrscht auch dort Ordnung.

Danke

Michael

von Oryx (Gast)


Lesenswert?

Hi,
also bei mir werden nicht soviele Register gesichert.

Mit welchen Optionen kompilierst Du.

Bei mir ist -Os aktiv.

Ergebnis:
00000056 <__vector_6>:
  56:  1f 92         push  r1
  58:  0f 92         push  r0
  5a:  0f b6         in  r0, 0x3f  ; 63
  5c:  0f 92         push  r0
  5e:  11 24         eor  r1, r1
  60:  8f 93         push  r24
  62:  9f 93         push  r25
  64:  af 93         push  r26
  66:  bf 93         push  r27
  68:  80 91 63 00   lds  r24, 0x0063
  6c:  90 91 64 00   lds  r25, 0x0064
  70:  a0 91 65 00   lds  r26, 0x0065
  74:  b0 91 66 00   lds  r27, 0x0066
  78:  01 96         adiw  r24, 0x01  ; 1
  7a:  a1 1d         adc  r26, r1
  7c:  b1 1d         adc  r27, r1
  7e:  80 93 63 00   sts  0x0063, r24
  82:  90 93 64 00   sts  0x0064, r25
  86:  a0 93 65 00   sts  0x0065, r26
  8a:  b0 93 66 00   sts  0x0066, r27
  8e:  bf 91         pop  r27
  90:  af 91         pop  r26
  92:  9f 91         pop  r25
  94:  8f 91         pop  r24
  96:  0f 90         pop  r0
  98:  0f be         out  0x3f, r0  ; 63
  9a:  0f 90         pop  r0
  9c:  1f 90         pop  r1
  9e:  18 95         reti

aus
SIGNAL(SIG_OVERFLOW0)
{
  timer0++;
}



Oryx

von Michael (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

mein Makefile ist oben.

Ich habe es jetzt in einer extra Datei und per include eingebunden.
Damit sind die wahnsinnig vielen pop und push verschwunden.

Dies scheint aber grundsätzlich nur aufzutreten, wenn man von
Interrupt-
einsprungadressen nochmal in eine Funktion überleitet.

Bei mir war das bei allen 3 Interupt's so.


Michael

von Stefan (Gast)


Lesenswert?

Und woher weisst Du, welche Register der Compiler jetzt benötigt? Das
musst Du ja jetzt selbst kontrollieren! Und die Fehler, die auftreten,
weil im IR ein Reg zuwenig gesichert wird, die will ich nicht finden
müssen ...

Am Besten ist es, Du verwendest im IR keine Unterprogramme. Dann kann
der Compiler auch optimieren. Vorrausschauend die Registerbelegung
eines Unterprogramms zu erfassen dürfte für jeden Compiler nahezu
unmöglich sein, spätestens dann, wenn die Routine in einem anderen File
steht.

Stefan

von MarkusS (Gast)


Lesenswert?

Hallo,

generell sollte man in Interrupt Routinen immer möglichst kurz und
klein halten.
Um sie möglichst kurz zu halen empfiehlt es sich keine Funktionsaufrufe
zu verwenden, da man hier zumindest den Sprung zur Funktion und den
Rücksprung einsparen kann.


Gruß MarkusS

von Peter D. (peda)


Lesenswert?

"Vorrausschauend die Registerbelegung
eines Unterprogramms zu erfassen dürfte für jeden Compiler nahezu
unmöglich sein, spätestens dann, wenn die Routine in einem anderen
File
steht."


Doch, der Keil C51 kann das.
Er legt für jede Funktion einen Eintrag der Registerverwendung an.
Wird eine Unterfunktion aufgerufen, dann schaut er in der Liste nach,
ob dazu ein Eintrag existiert.
Wird die Funktion aber erst später compiliert, existiert kein Eintrag
und er geht von der maximalen Registerverwendung aus.
Stellt er später fest, daß er zu pessimistisch war, compiliert er
nochmal und dann weiß er aber schon die Registerverwendung aller
Funktionen. Je nach Aufruftiefe können aber noch weitere Durchläufe
folgen.
Daher ist es besser, man macht ihm die Arbeit einfacher und schreibt
die Unterfunktionen möglichst vor den Aufrufer.


Peter

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.