Die Phase, in der ein Compiler sich entscheidet, welche Variable er in welches Register ablegt, nennt man Reload-Phase.
Sounds trivial, aber es ist eine der komplexesten und trickreichsten Phasen beim Compilieren -- zumindest dann, wenn man es gut machen will und die Compilerbauer nicht faul sind.
Aus einer lokalen Variable kann folgendes werden:
- Sie wird in einem Register gehalten
- Sie wird auf dem Stack angelegt. Bei vielen lokalen Variablen braucht die Funktion einen Frame, in dem lokale Variablen angelegt werden.
- Sie wird wegoptimiert und belegt überhaupt nix, ist nicht mal von einem Debugger aus lesbar
- statische lokale Variablen werden im RAM angelegt und "überleben" Funktionsaufrufe
Was Funktionen angeht, so wird in der ABI (Application Binary Interface) beschrieben, welche Register durch eine Funktion einfach benutzt werden und verunstaltet werden dürfen (call-used bzw. call-clobbered Regs) und welche einen Funktionsaufruf unbeschadet überstehen (call-saved Regs).
Bei einer Funktion mit 40 Variablen wird keine als volatile angelegt, es sei denn, du sagst es.
Eine Interrupt Service Routine (ISR) sichert die verwendeten Register sowie das SREG, vor Verlassen der ISR werden die Regs wieder hergestellt. Teilweise werden aber mehr Register gesichert als wirklich nötig wäre, etwa wenn in einer ISR eine Funktion aufgerufen wird. Der Compiler sieht die Funktion nur als Black Box, er weiß i.d.R. nicht was darin abgeht; muss also auch alle call-used Regs sichern, weil die Funktion sie zerstören darf bzw. kann.
Ähnlich geht eine Funktion vor, wenn sie ein call-save Reg verwenden will. damit es nach ihrem Aufruf den ursprünglichen Wert hat, wird es im Prologue gesichert und im Epilogue wieder hergestellt.






Zitieren

Lesezeichen