Sorry, aber ein besser Titel ist mir nicht eingefallen.
Nachfolgender im WinAVR GCC
1
SIGNAL(SIG_OUTPUT_COMPARE1B) {
2
3
uint8_t i = DisplayRow;
4
if (++i >= 15) i = 0; // 100Hz
5
DisplayRow = i;
6
// 1.)
7
PORTC = (PORTC & ~ROW_MASK) | (i << 3);
8
// 2.)
9
uint8_t* p = (uint8_t*)&DisplayRAM[i];
10
uint8_t v;
11
SPDR = p[1];
12
v = p[0];
13
while (!(SPSR & (1 << SPIF)));
14
SPDR = v;
15
v = p[3];
16
while (!(SPSR & (1 << SPIF)));
17
SPDR = v;
18
v = p[2];
19
while (!(SPSR & (1 << SPIF)));
20
SPDR = v;
21
v = p[5];
22
while (!(SPSR & (1 << SPIF)));
23
SPDR = v;
24
v = p[4];
25
while (!(SPSR & (1 << SPIF)));
26
SPDR = v;
27
v = p[7];
28
while (!(SPSR & (1 << SPIF)));
29
SPDR = v;
30
v = p[6];
31
while (!(SPSR & (1 << SPIF)));
32
SPDR = v;
33
v = p[9];
34
while (!(SPSR & (1 << SPIF)));
35
SPDR = v;
36
v = p[8];
37
while (!(SPSR & (1 << SPIF)));
38
SPDR = v;
39
while (!(SPSR & (1 << SPIF)));
bei 1.) wird ein Spaltentreiber am PortC angesprochen
bei 2.) wird per ISP Shiftregister gefüttert
Der indizierte Zugriff auf das array p[] -> Displayram ist drinnen
damit der GCC mit ld reg,z+c also Speicherzugriff mit konstantem Offset
auf den Displayspeicher zugreifen kann.
Oh freude der erzeugte ASM dafür sieht auch so aus:
1
479 .L79:
2
480 039e 779B sbis 46-0x20,7
3
481 03a0 FECF rjmp .L79
4
482 03a2 8FB9 out 47-0x20,r24
5
483 03a4 8581 ldd r24,Z+5
super.
Aber beim Zugriff 1.) das (i << 3) baut GCC per int und demzufolge
unsinnigerweise
1
447 0362 25B3 in r18,53-0x20
2
448 0364 2778 andi r18,lo8(-121)
3
449 0366 A32F mov r26,r19
4
450 0368 BB27 clr r27
5
451 036a CD01 movw r24,r26
6
452 036c 880F lsl r24
7
453 036e 991F rol r25
8
454 0370 880F lsl r24
9
455 0372 991F rol r25
10
456 0374 880F lsl r24
11
457 0376 991F rol r25
12
458 0378 282B or r18,r24
13
459 037a 25BB out 53-0x20,r18
viel zu ineffizient.
So dachte ich mir, sagste es dem GCC explizit was du von ihm willst und
änderte 1.) um in
1
PORTC = (PORTC & ~ROW_MASK) | (uint8_t)(i << 3);
also mit explizitem Typcast. Daraufhin verändert sich im ASM aber die
Registerauslastung und der ASM für 2.) sind danach so aus:
1
480 .L79:
2
481 03a0 779B sbis 46-0x20,7
3
482 03a2 FECF rjmp .L79
4
483 03a4 8FB9 out 47-0x20,r24
5
484 03a6 1596 adiw r26,5
6
485 03a8 8C91 ld r24,X
7
486 03aa 1597 sbiw r26,5
Das sind immerhin schon 4 Bytes und 2 Takte mehr als vorher !
So nun meine Frage: gibt es in der Art & Weise wie GCC einen C Source
in einen ASM übersetzt irgendeine Logik, eine nachvollziehbare Logik ?
Wie kann ich GCC sagen das er p[] in Register ZH:ZL speichern soll
damit er auch explizit per "LD reg,Z+constant" arbeitet.
Normalerweise hätte ich vermutet das der Compiler so smart genug ist
das selber zu erkenen. Macht er ja auch, aber nicht immer und nicht mit
nachvollziehbarer Logik.
Gruß Hagen
Einfache Compiler => einfache Regeln.
Komplexe Compiler => komplexe Regeln.
Wenn du einen einfach nachvollziehbaren Compiler willst, nimm nicht
GCC, sondern z.B. SDCC. Am GCC wird seit 2 Jahrzehnten herumgefeilt mit
Optimierungen aller Art. Mit Architekturen wie PowerPC, Itanium usw. im
Auge, AVR ist da nur ein kleines Nebenprodukt.
Naja halb so wild, soooo schlecht ist der GCC nun auch wieder nicht.
Wahrscheinlich ist es einfach so das mir noch die "Erfahrungen" mit
dessen Eigenheiten fehlen ;) Es ist halt so das mir beim Anblick so
mancher LST Datei des GCCs sofort einige Optimierungen auffallen die im
Grunde selbstverständlich sein sollten. Nun möchte man aber den Vorteil
eines C Sources nicht zerstören indem man dann doch wieder alles in
Assembler codet oder aber im C Source lauter Typcast, register,
volatile Deklarationen reinbaut so das er nicht mehr lesbar ist.
Gruß Hagen
Ich verstehe nicht wo dein Problem liegt, du machst dir sorgen um ein
paar Takte aber wartest ständig auf die SPI schnittstelle. Was dauert
wohl länger???
und das ganze machst du auch noch in einem Timerinterrupt?
EIN INTERRUPT IST KEIN THREAD!!!!!
Du bist ganz offensichtlich im Timer OCR Modus. Wenn der Interrupt
ausgelöst wird setzt dein Timer automatisch seinen Counter auf 0 und
zählt weiter noch während du im Interrupt bist. Sollte er wieder OCRB
erreichen wird eine Interruptbedingung erfüllt. Je nachdem ob du noch
im Interrupt bist und das Interruptflag gesetzt ist wirst du den
folgenden verpassen. Solltest du noch von irgendwo anders auf die
SPI-schnittstelle zugreifen wird es richtig interessant.
Setze ein Flag in der Interruptroutine und polle dieses im
Hauptprogramm, wenn es gesetzt ist greife auf die SPI-schnittstelle
zu.
(Flag mit volatile deklarieren)
@Wolfram:
ganz so schlimm ist das nicht. Der SPI kann bis zu Quarzfrequenz/2
konfiguriert werden. Dann braucht 1 * Senden 16 Takte. So schnell
schaffst Du es nicht, einen IR zu beenden und einen neuen zu starten -
jedenfalls nicht in C. Und Hagen macht es ja auch ganz richtig: nach
dem Starten per SPDR wird v schon wieder mit dem nächsten Wert geladen,
bevor auf SPIF gewartet wird. Die Wartezeit wird also möglichst immer
ausgenützt.
@Hagen:
Du kannst Dir das ganze Geshifte sparen, wenn Du DisplayRow statt um 1
um 8 erhöhst:
uint8_t i = DisplayRow;
i += 8;
i &= ROW_MASK; // nur zur Sicherheit, damit untere
// 3 Bits immer NULL sind (kann entfallen)
if (i >= (15*8)){
i = 0;
}
DisplayRow = i;
PORTC = (PORTC & ~ROW_MASK) | i;
Gruß, Stefan
@Wolfram: du kennst doch garnicht meinen gesammten Source und weist
somit auch nicht wie ich was konfiguriert habe.
Ja du hast Recht das ich dies in der ISR mache und ich tue dies aus
gutem Grunde. SPI läuft mit 8 Mhz, AVR mit 16 MHz, ergo warten die
Schleifen exakt 16 Takte. Der Timer läuft zwar mit Prescaler 1, aber
als TOP das OCR1A Register mit 10696 was dann ein Interval von 5ms
ergibt. Die Kommuniktation per SPI darf durch keine andere Sache in der
MCU unterbrochen werden, muß im zeitlichen Interval kontinuierlich
erfolgen. Fazit: alle 5 Millisekunden sendet der AVR per SPI 10 Bytes
was dann 625ns dauert und nicht durch andere ISRs wie die vom UART/ADC
unterbrochen werden darf. Die Kommmunikation über das SPI hätte auch
ausgelagert werden können, aber dann müsste sie denoch per cli/sei vor
Interrupts geschützt werden, zusätzlich synchronisiert werden über
Flags, und auf Grund der unterschiedlichen Priotitätenverteilung in der
Mainloop wäre denoch nicht sichergestellt das das Timing dann stimmt.
Also kann ich sie gleich in die ISR reinschreiben, das sparrt Resourcen
und ist strikter im Timing. Bei dieser Kommunikation geht es um die
multiplexte Ansteuerung eines LED Displays. Ein Ausfallen der
SPI-Kommunikation würde die Darstellung enorm beinflussen, da die
ON/OFF Steuerung sowie die Shiftregister-Latch-Steuerung des Displays
am OCR1B Pin hängt. Das Toggeln dieser Steuerleitungen hängt also
direkt mit dem Timer zusammen und die SPI Datenübertragung zwangsläufig
auch. Und ja, das SPI ist exklusiv nur für die Displaysteuerung
verantwortlich.
@Stefan: Das könnte ich machen, aber ;)
Erstens ist dieser Source ein vorläufiger auf einem Testboard. Auf dem
fertigen Board liegt der Spaltentreiber garnicht an PORTC sondern PORTA
und dort auf PIN0 bis PIN3. Ich habe das also schon eingeplant.
Zweitens wird mit diesem Display-Spalten-Treiber auch ein analoger
Switch -> HC4051 angesteuert der über den ADC kapazitive Sensortasten
auswertet. Nun gleich nach dem obigen Sourcecode-Ausschnitt wird diese
Sensorauswertung mit dem ADC gestartet. Ergo: ich benötige i =
DisplayRow in obiger Form noch für andere Zwecke.
Es geht mir hier nicht um den Source im speziellen sondern nur um den
sich ergebenden Effekt im WinAVR GCC, und dieser kann in jedem Source
auftreten ;) Den Source sollte man eher als Beispiel für die
Begutachtung der Fähigkeiten des GCCs betrachten. Übrigens aus diesem
Source
1
SIGNAL(SIG_OVERFLOW1) {
2
3
uint8_t i = DisplayRow;
4
uint8_t v = i +1;
5
if (v >= 15) v = 0;
6
// 1.)
7
register uint8_t* p asm("r30") = (uint8_t*)&DisplayRAM[v];
8
v = p[1];
9
SPDR = v;
10
v = p[0];
11
while (!(SPSR & (1 << SPIF)));
12
SPDR = v;
13
v = p[3];
14
while (!(SPSR & (1 << SPIF)));
15
SPDR = v;
16
v = p[2];
17
while (!(SPSR & (1 << SPIF)));
18
SPDR = v;
19
20
... hier gehts noich weiter !
erzeugt der GCC folgendes bei 1.)
[/code]
483 .L67:
484 0398 8AE0 ldi r24,lo8(10)
485 039a 989F mul r25,r24
486 039c D001 movw r26,r0
487 039e 1124 clr r1
488 03a0 A050 subi r26,lo8(-(DisplayRAM))
489 03a2 B040 sbci r27,hi8(-(DisplayRAM))
// X -> Z
490 03a4 FD01 movw r30,r26
// laden über X und mit 2 unnötigen Additionen !
491 03a6 1196 adiw r26,1
492 03a8 9C91 ld r25,X
493 03aa 1197 sbiw r26,1
494 03ac 9FB9 out 47-0x20,r25
// laden über X, obwohl im Source p[] -> register asm("r30")
explizit
495 03ae 9C91 ld r25,X
496 .L68:
497 03b0 779B sbis 46-0x20,7
498 03b2 FECF rjmp .L68
499 03b4 9FB9 out 47-0x20,r25
// super, endlich geschnallt das wollte ich schon vorher im ASM sehen
500 03b6 9381 ldd r25,Z+3
[/code]
wie man sieht packt er die Berechunung des Zeigers p[] in Register X
rein um diesen dann nach Z zu kopieren. Nungut, dieser unnötige Umweg
würde mich ja nicht sonderlich stören wenn er nicht im ersten Zugriff
-> p[1] noch umständlich mit X hantieren würde und gleich später ganz
sauber mit LDD reg,Z+constant arbeiten würde. Defakto eine Inkonsistenz
in der Optimierung, bzw. Anti-Optimierung.
Bevor wieder gemeckert wird. Es geht mir hier nicht um den nonplusultra
optimierten Code, sondern um das Verständnis WIE der GCC zu WELCHEM
Zeitpunkt WAS macht. Denn das ist, meine ich, absolut unverhersehbar
beim GCC, und hat nichts mit "Optimierungen" zu tun.
Gruß Hagen
@Jörg:
super das ist doch mal ein Angebot, danke. Im Attachment die Sourcen.
Allerdings musst du dich da jetzt nicht sonderlich reinknienen da der
Source nur vor-experimenteller Natur ist. Ich studiere halt nur
parallel zur Entwicklung des C Sources wie sich der GCC im ASM verhält
damit ich ein Gefühl dafür bekomme. Das hat mir vor vielen Jahren auf
Intel CPUs, vor einigen Jahren auf Motorola CPUs geholfen und wird mir
heute beim GCC helfen. Letzendlich ist es nur ein par Minuten Aufwand
eine konzeptionell fertige ISR nach GCC-ASM zu portieren, dann haben
sich die "Probleme" erledigt.
Gruß hagen
Das bsondere am AVR ist es ja das man Pi*Daumen behaupten kann "ein
kurzer Code ist auch ein schneller Code".
Gruß Hagen
PS: wollt ich nur mal los werden ;)
@Hagen:
deine Ausführungen zeigen das du das ganze durchgerechnet hast und das
du verstehst was du tust. Wenn ich den ganzen Source gesehen hätte
,hätte ich mir meine Bemerkung verkniffen.
Es kommt nur öfters vor, das einige Interrupts mit threads verwechseln
oder als Krönung noch ein printf in einem Timerinterrupt benutzen und
danach sah das Codestück aus.
Allerdings hast du einen Rechenfehler:
>Fazit: alle 5 Millisekunden sendet der AVR per SPI 10 Bytes>was dann 625ns dauert und nicht durch andere ISRs wie die vom
UART/ADC
>unterbrochen werden darf.
10 Byte =80 Bit,SPItakt 8Mhz=0,125us
0,125ms*80=10us
natürlich immer noch im sicheren Bereich...
Wenn deine SPIübertragung mit voller Geschwindigkeit laufen soll ohne
unterbrochen zu werden benutze doch den SPItransferinterrupt. Das
könnte insgesamt besser werden.
Compiliert leider nicht. Erstens nimmst du Backslashes in
#include <avr\io.h>
Don't do that. Windows akzeptiert auch Vorwärtsstriche, damit
machst du anderen die Arbeit leichter. Dann geht's weiter mit:
uart.c: In function 'uart1_Read':
uart.c:457: error: assignment of read-only location
Habe mal das "const" da rausgeworfen, sieht irgendwie aber
suspekt aus. Hier noch die Warnungen:
main.c:383: warning: passing argument 1 of 'uart1_Write' from
incompatible pointer type
main.c:384: warning: passing argument 1 of 'uart1_Write' from
incompatible pointer type
main.c:385: warning: passing argument 1 of 'uart1_Write' from
incompatible pointer type
main.c:386: warning: passing argument 1 of 'uart1_Write' discards
qualifiers from pointer target type
main.c:387: warning: passing argument 1 of 'uart1_Write' discards
qualifiers from pointer target type
Solltest du sicher auch auflösen.
Für meine Begriffe hast du den Code in main.c auch sehr reichlich
mit "volatile" ausgeschmückt... Ich bin damit immer sehr
vorsichtig, weil das eben alle und jede Optimierung verhindert.
Der generierte Code für den Overflow-Handler ist im Anhang.
Btw., stell dich allmählich von SIGNAL uns SIG_XXX auf ISR
und XXX_vect um, wenn du neuen Code schreibst.
@Jörg:
Sagte doch: das ist hingeschlampter Source um das SPI in Zusammenhang
mit der Auswertung kapazitiver tastenfelder zu testen ;)
>>>
uart.c: In function 'uart1_Read':
uart.c:457: error: assignment of read-only location
Habe mal das "const" da rausgeworfen, sieht irgendwie aber
suspekt aus. Hier noch die Warnungen:
<<<
Danke, das const gehört da wirklich nicht rein.
>>>
Für meine Begriffe hast du den Code in main.c auch sehr reichlich
mit "volatile" ausgeschmückt... Ich bin damit immer sehr
vorsichtig, weil das eben alle und jede Optimierung verhindert.
<<<
Das war auch der Sinn ;) Einfach um sicher zugehen und das ich mich auf
das Wesentliche -> die experimentelle Schaltung -> konzentrieren kann.
Wie man in den vorherigen Postings ersehen kann bin ich halt noch nicht
so sicher bei der Frage was macht GCC aus meinem Source, ergo um
unnötigen Ärger vorzubeugen die vielen volatiles. Ich mag es nicht
sonderlich wenn ich Fehler erstmal bei mir und meinem Source suche,
später feststellen zu müssen das irgendwas anderes, zb. der Compiler,
Mist gebaut hat. Das ist jetzt keine Unterstellung dem GCC gegenüber
sondern zb. Erfahrungen die ich erst letztens ständig mit einem
Compiler für Motorola CPUs für Palm Handhelds gemacht habe.
>>>
main.c:383: warning: passing argument 1 of 'uart1_Write' from
incompatible pointer type
Solltest du sicher auch auflösen.<<<
Korrekt, ist Debugcode der niemals in einem Produktivcode auftauchen
wird ;) Letzendlich macht der Compiler das daraus was ich ihm gesagt
habe.
>>>
Btw., stell dich allmählich von SIGNAL uns SIG_XXX auf ISR
und XXX_vect um, wenn du neuen Code schreibst.
<<<
Das ist mir neu, habe ich da was verpennt ?
@Wolfram: ja da hast du Recht (10*16*62.5ns == 10us), und die ISR wird
auch nicht alle 5ms aufgerufen sondern alle 666µs, sorry.
Gruß Hagen
@Jörg: so obiger Post bezieht sich ja nicht auf das "Problem" selber
;)
Also
1
.L66:
2
ldi r24,lo8(10)
3
mul r25,r24
4
movw r26,r0
5
clr r1
6
subi r26,lo8(-(DisplayRAM))
7
sbci r27,hi8(-(DisplayRAM))
8
movw r28,r26
9
ldd r24,Y+1
10
out 47-0x20,r24
11
ld r24,X
12
.L68:
13
sbis 46-0x20,7
14
rjmp .L68
15
out 47-0x20,r24
16
movw r28,r26
17
ldd r24,Y+3
hier scheint er aber wieder auf X zuzugreifen, was ich nicht ganz
nachvollziehen kann.
Wenn ich per
register type name asm("r30");
dem GCC explizit vorgebe das er das so benutzen soll, warum macht er es
dann nicht ?
Dann ist mir noch aufgefallen das die Berechnung von p[] eigentlich
auch ein par Umwege geht. Kann GCC nicht gleich die Berechnungen über
Register ZH:ZL machen ?
Zb. das "clr r1" ist doch überflüssig, oder ?
Gruß Hagen
@Jörg:
vergiss den Rest das ist mir jetzt unwichtig.
>>>
Btw., stell dich allmählich von SIGNAL uns SIG_XXX auf ISR
und XXX_vect um, wenn du neuen Code schreibst.
<<<
Das ist wichtiger. Ich habe mir die WinAVR Page auf SourceForge
angeschaut, finde aber nichts darüber !?
Gruß Hagen
Hallo,
ab AXR-LIBC Version 1.4 (oder früher?) wird das SIGNAL() als deprecated
bezeichnet. Man soll dann ISR() verwenden. Was sich da alles geändert
hat, findet man in der avr-libc-user-manual. Bei dem diesjährigen
Winavr ist das schon umgesetzt.
Ich bin auch gerade dabei ein paar alte Programme zu entrümpeln.
Gruß Elektrikser
Man muß es dem GCC zugestehen, daß er an manchen Stellen ziemlich dumm
ist und nicht optimiert, sondern im Gegenteil sogar völlig nutzlos auf
16 Bit erweitert:
1
if((i&0xFE)==0xFE)
2
fa:9927eorr25,r25
3
fc:8e7fandir24,0xFE
4
fe:9070andir25,0x00
5
100:8e3fcpir24,0xFE
6
102:9105cpcr25,r1
7
104:11f4brne.+4
8
PORTB=0xFE;
9
106:8eefldir24,0xFE
10
108:88bbout0x18,r24
11
10a:0895ret
Ist es nicht putzig, wie er R25 auf 0 setzt (eor r25,r25) und dann
sicherheitshalber gleich nochmal (andi r25,0x00). Danach zweifelt er
aber immer noch daran, daß es 0 ist und testet, ob es wirklich 0 ist
(cpc r25,r1).
Er hat nun zwar umständlich festgestellt, daß R24 = 0xFE ist, aber ist
sich wiederum nicht sicher und setzt es daher nochmals auf 0xFE.
Irgendwie scheint er keinerlei Vertrauen zu haben, daß der AVR Befehle
auch wirklich ausführt.
Manchmal kann er aber auch gut optimieren.
In Deinem Fall dürfte aber das SPI den Löwenanteil an CPU-Zeit
ausmachen (160 Zyklen), so daß eine Optimierung keinen merkbaren Effekt
hat. Du solltest daher der Lesbarkeit den Vorzug geben.
Ich würde aber beim Schreiben schon die Bytes in der richtigen
Reihenfolge ablegen, dann kann man statt Spaghetticode einfach ne
Schleife machen:
[/C]
SIGNAL(SIG_OUTPUT_COMPARE1A) {
unsigned char i, *ptr;
ptr = DisplayRAM + DisplayRow;
for( i = 10; i; i-- ){
while (!(SPSR & (1 << SPIF)));
SPDR = *ptr++;
}
}
[/C]
Peter
Hi Peter,
Die Bytes liegen in korrekter Reihenfolge und werden auch in korrekter
Reihenfolge gesendet. Dazu ein bischen mehr Hintergrundinformationen.
5x 16 Bit Shiftregister, kaskadiert. Auf Grund ihrer Bauform und dem
Boardlayout steuert Register 5
Pixel 0 rot/grün, Pixel 1 rot/grün, Pixel 2 rot/grün usw.
Dabei liegt Pixel 0 rot am Ausgang 16, Pixel 0 grün am Ausgang 15,
Pixel 15 am Ausgang 14 usw.
Das SPI sendet LSB first.
Die Pixel sind im DisplayRAM als 16 Bit unsigned gespeichert, also Bit
0 DisplayRAM[0, 0] ist Pixel 0 rot, erste Zeile.
Das ist so konstruiert weil ich die SetPixel(X,Y,Farbe) Funktion schon
jetzt so geplant habe:
setPixel(x,y,Farbe)
{
DisplayRam[y, X div 16] = Farbe << (x % 16)
}
Du kannst die vorstellen das div/mod 16 in ASM gut zu optimieren ist ->
SWAP.
Der versetzte Lesezugriff auf dem DisplayRAM erfolgt weil dadurch die
Warteschleife die aufs SPI wartet um 2 takte verkürzt wird. Ich lade
also, wenn ich noch aufs SPI warte schonmal das nächste Datenbyte. So
spare ich 2 taktzyklen in der Gesamtwartezeit.
Ist also alles in bester Ordnung:
1.) Datenstuktur im DiplayRAM ist optimiert auf effizientes Pixel
setzen
2.) Hardware ist angepasst auf optimales Boardlayout
3.) SPI sendet Daten so wie sie sein müssen das zum Schluß auch der
Pixel 0 -> DisplayRAM[0,0] bit 0,1 auch als Pixel links oben auf dem
Display leuchtet.
Dein Vorschlag mit der Schleife würde zwar weniger Code benötigen dafür
aber mindestens 2 Takte pro Byte mehr.
So, und wie ich es oben schon sagte: mir geht es nicht um meinen Code
ansich, ich weiß was ich wie tue und wenn ich nicht mehr weiter weis
frage ich euch, versprochen. In diesem Falle ging es mir nicht um den
Sinn meines Sources sondern um die Frage was der GCC daraus macht.
Gruß Hagen
Achso: die 160 taktzyklen für das SPI lassen sich nicht vermeiden. Ist
einfach eine Timing Frage beim Refesh/Multiplexen des Displays. Davon
abgesehen ist das nicht weiter dramatisch da diese 10µs +50µs
kapazitive Sensortasten nur alle 666µs benötigt werden und den Rest der
Zeit verschläft der AVR eh meistens. Die par DFC77 Daten und das
aktualisieren der Uhrzeit und das Änderen der Darstellung auf dem
Display fallen zeitlich nicht ins Gewicht.
Gruß Hagen
Nochwas: wenn du dem Compiler mit asm("r30") irgendwie deinen
Willen von hinten herum aufdrängst, brauchst du allerdings
nicht wirklich erwarten, dass der Optimizer das in irgendeiner
Form in seine Betrachtung einzubeziehen in der Lage ist. Solange
er auf eine derartige Optimierung nicht von selbst gekommen wäre,
wird er mit deiner ,,Hilfestellung'' nichts anfangen können.
Ansonsten hat der GCC-4-Code zwar ein paar überflüssige movw
r28,r26-Befehle, ansonsten fand ich ihn aber so schlecht nicht.
Insbesondere sind all die überflüssigen Schiebereien dort nicht
mehr drin.
Am Optimizer vom GCC 3 wird eh' keiner mehr was ändern, der wird
allerbestenfalls noch Bugfixes bekommen.