Ja, hier werden die Register unnötigerweise gesichert, weil sie nicht verändert werden.

IMHO ist das eine Unzulänglichkeit in GCC (also nicht nur in avr-gcc).

Ich kann die erläutern, wo das Problem herkommt, und wo du es fixen kannst:

Du übersetzt mit -da -dp -save-temps. Das erzeugt dir zig RTL (register transfer language) dumps.
Einer der komplexesten Teile von GCC (und vieler anderer optimierender Compiler) ist die Register-Allokierung. In diesen Passes werden die unendlich vielen Pseudo-Register, in denen die Werte gehalten werden, auf die endlich vielen Hard-Register der Maschine abgebildet. Falls die Hard-Register nicht ausreichen, landen die Werte in Stack-Slots und müssen zur Verabreitung vom Stack geladen werden, wofür dann wieder andere Hard-Register frei geschaufelt werden müssen, etc.

In Pass lreg (local register allocation = reg-alloc innerhalb von basic blocks) ist noch alles ok, zB
Code:
(insn 5 4 6 0 (set (reg/v:SI 44 [ c ])
        (reg:SI 14 r14 [ c ])) 14 {*movsi} (nil)
    (expr_list:REG_DEAD (reg:SI 14 r14 [ c ])
        (nil)))
(reg 44) ist ein SI-Pseudo (also 32-Bit) und wird geladen vom SI-Hard-Reg 14, einem incoming arg (=c) von foo. In dieser insn stirbt R14 (also R14...R17), weil es jetzt ja in R44 steht. Ich vereinfache mal das insn-Pattern auf das Wesentliche:

Code:
(set (reg:SI 44)
     (reg:SI 14))
In Pass greg+postreload (global register allocation) wird daraus
Code:
(set (reg:SI 14)
     (reg:SI 14))
d.h. GCC allokiert R44 nach R14 (eine gute Wahl) und in flow2 wird erkannt, daß insn 5 trivial ist und weggeworfen. Allerdings lebt R14 weiterhin, weil es ja verwendet wird (wenn auch nirgends explizit gesetzt wird).

In ./gcc/config/avr/avr.c:function_prologue() und :function_epilogue() wird der asm-Code für Funktions-Prolog und -Epilog ausgegeben, der sich u.a. darum kümmert, Regs die leben *und* call save sind (als nach einer Funktion unverändert vorliegen) zu sichern, d.h. dort werden die überflüssigen push/pop emittiert.

Die Info, ob ein (pseudo oder hard) Reg lebt, wird in regs_ever_live[] gemerkt. Und dort steht eben nur, ob ein Reg lebt, aber nicht, ob es verändert wird oder nicht.

Um den Code zu verbessern, müsste man also an dieser Stelle nicht auf regs_ever_live[] testen, sondern eine neue live analysis durchführen und testen, ob für diese Register REG_N_SETS() gleich Null ist und einen neuen avr-gcc generieren (bzw ein neuer cc1, das reicht schon). Möglicherweise sieht GCC bzw. die zu REG_N_SETS() betragenden Funktionen in ./gcc/regclass.c, daß R14 zu den virtual-incoming-args Registern gehört, und zählen es zu den gesetzten Registern hinzu.

Falls du an GCC nicht selbst Hand anlegen willst, kannst du bei http://gcc.gnu.org eine feature-request machen und bei der GCC 4.3 hast du schon besseren Code. (AFAIK ist momentan jedoch Konsolidierung und ein feature freeze).