Forum: Compiler & IDEs Codesize WinAVR 20060421 vs WinAVR-20090313


von Sebastian____ (Gast)


Lesenswert?

Hallo,
ich habe ein kleines Problem. Bei einenm Projekt mit einem Mega8 habe 
ich den Flash fast voll.
Aber zwischen den beiden Compilervarianten 20060421 <-> 20090313 erzeugt 
der WinAvr von 2006 einen 500byte kleineren code als der aktuelle 
WinAvr.

Die optimierung ist jeweils schon auf "s" eingestellt.
Aus dem Map file werde ich nicht richtig schlau, wie bekomme ich denn 
eine Ausflistung aus dem COmpiler welche funktion wie viel speicher im 
Controller belegt?

MfG
Sebastian

von (prx) A. K. (prx)


Lesenswert?

Probier mal diese Optionen aus:
-fno-inline-small-functions
-fno-split-wide-types
-fno-tree-scev-cprop

von (prx) A. K. (prx)


Lesenswert?

Sebastian____ wrote:

> Aus dem Map file werde ich nicht richtig schlau, wie bekomme ich denn
> eine Ausflistung aus dem COmpiler welche funktion wie viel speicher im
> Controller belegt?

Eben aus dem Mapfile. Ohne es zu sehen ist die Analyse davon aber noch 
deutlich schwieriger ;-).

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Vielleicht hilft AVR-GCC-Codeoptimierung bzw. kann man den Artikel 
für aktuelle AVR-GCC Versionen updaten (nur 3 Überarbeitungen 2008/2009 
bisher).

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Sebastian____ wrote:
> Hallo,
> ich habe ein kleines Problem. Bei einenm Projekt mit einem Mega8 habe
> ich den Flash fast voll.
> Aber zwischen den beiden Compilervarianten 20060421 <-> 20090313 erzeugt
> der WinAvr von 2006 einen 500byte kleineren code als der aktuelle
> WinAvr.

Probleme bereiten gerne eeprom_read_block und eeprom_wriet_block aus der 
avr-libc, sie mit -mcall-prologues übersetzt sind.

Der Code bekommt man mit -fno-inline-functions idR kleiner, ob 
-fno-split-wide-types etwas hilft, hängt von der konkreten Quelle ab.

Daß gcc 4.x größeren Code macht als 3.4.6 ist bekannt. Insbesondere 
führen bei 4.x Loop-Unrolling/Peeling, If-Conversion und Loop-Invariant 
Motion  auch zu größerem Code.

Johann

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Stefan B. wrote:
> Vielleicht hilft AVR-GCC-Codeoptimierung bzw. kann man den Artikel
> für aktuelle AVR-GCC Versionen updaten (nur 3 Überarbeitungen 2008/2009
> bisher).

Mit Verlaub: Was da steht ist in bezug auf foo-gcc ziemlicher Humbug. 
Der Artikel basiert euf einer Application-Note von Atmel für den IAR.

Johann

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Johann L. wrote:

> Mit Verlaub: Was da steht ist in bezug auf foo-gcc ziemlicher Humbug.
> Der Artikel basiert euf einer Application-Note von Atmel für den IAR.

Und jetzt? Soll man den Artikel entfernen oder kann man ihn verbessern? 
Oder möchtest du deine Äusserung belegen, darüber diskutieren oder 
einfach nur als Statement so stehen lassen?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Stefan B. wrote:
> Johann L. wrote:
>
>> Mit Verlaub: Was da steht ist in bezug auf foo-gcc ziemlicher Humbug.
>> Der Artikel basiert euf einer Application-Note von Atmel für den IAR.
>
> Und jetzt? Soll man den Artikel entfernen oder kann man ihn verbessern?
> Oder möchtest du deine Äusserung belegen, darüber diskutieren oder
> einfach nur als Statement so stehen lassen?

Das kannst du einfach belegen, indem du zum Beispiel dan am 
Artikel-Anfang  angegebenen Codeschnippsel gcc zum Fraß vorwirfst und 
das Resultat mit dem angeblichen Resultat vergleichst. Du wirst 
feststellen, daß gcc absolute Adressierung verwendet.

Mit -mint8 ist man wie gesagt nicht mehr interlink-fähig mit der libc 
und der libm. Die libgcc sollte passen, allerdings muss einem klar sein, 
daß es durch -mint8 massive Verschiebungen in der Semantik des 
C-Programmes gibt. Man lasse sich zB mal sizeof (long int) zusammen mit 
-mint8 anzeigen.

Ähnliche Fallstricke haben auch globale Registervariablen. Wenn man 
nicht genau weiß, was das für Konsequenzen hat bzw. haben kann, fliegt 
einem alles um die Ohren. Fieserweise nicht nach den Änderungen, sondern 
ne Woche später nach ner Änderung an einer ganz anderen Codestelle. Und 
dann sucht man sich den Wolf bis der Arzt kommt...

Natürlich können diese Techniken zu kleinem und schnellem Code 
beitragen, aber globale Registervariablen non-chalant zu empfehlen, 
finde ich schon  unfair gegenüber jemand, der sich mit den Nebeneffekten 
und Implikationen nicht genauestens auskennt, und dazu dürften die 
meisten Leser der Seite gehören. Globale Registervariablen sind eben was 
komplett anderes als globale Variablen!!

Und ganz am Rande: Auch bei -O0 führt gcc Optimierungen durch. Er faltet 
zum Beispiel Konstanten. Beispiel: a=1+2 wird zu a=3. Und was das zu 
"Nullinitialisierung" steht, muss man nicht wirklich verstehen...

Johann

von Hagen R. (hagen)


Lesenswert?

@Johann: und warum änderst du dann nicht den Wiki Beitrag, wenn du schon 
so sinnvolle Hinweise hast ?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Hagen Re wrote:
> @Johann: und warum änderst du dann nicht den Wiki Beitrag, wenn du schon
> so sinnvolle Hinweise hast ?

Mit indirekter Adressierung lässt sich durchaus merklich Code sparen -- 
wenn man es richtig einzusetzen weiß. Dafür gibt es allerdings keine 
allgemeinen Tipps, schon garnicht wenn man nur ein paar Quellzeilen hat 
mit davor und dahinter nur Pünktchen, Pünktchen, Pünktchen.

Wenn es so einfach wäre, das zu beschreiben, dann wär es schön längst im 
AVR-Teil von GCC, glaub mir.

Es hängt davon ab
-- wieviel Zugriffe von welcher Breite geschehen
-- ob dazwischen Funktionsaufrufe sind oder nicht
-- ob die Funktion eine ISR ist oder nicht
-- ob und wieviele andere indirekte Zugriffe geschehen (dazu gehört auch 
LPM)
-- wie hoch die Registerlast ist
-- wie groß die auftretenden Offsets sind
-- etc.

Last not least: es bringt auch nix, ein Symbol in ein Register zu 
forcieren, wenn gcc das Register dann zb nach R14 allokiert. Dann hat 
man zwar das Symbol in einem Register und es werden indirekte Zugriffe 
gemacht, aber vor jedem Zugriff wird der Zeiger in ein Pointer-Register 
kopiert. Code hat man dann keinen gewonnen, dafür nur Zeit verloren.

Was gcc angeht, so sind die Kosten absolute Adressierung versus 
indirekte Adressierung erst nach erfolgter Registerallokierung bekannt. 
Aber danach sind die Instruktionen und Register so verwoben, daß die 
Adressierungsart nicht mehr geändert werden kann. Vor der 
Registerallokierung kennt man aber die Kosten noch nicht, weil den 
Registern idR noch keinem Hardware-Register zugeordnet sind. Ich hätte 
noch nichtmal ne Idee, wo man in gcc diese Optimierung einbauen könnte. 
Expand (also direkt nach dem Übergang GIMPLE->RTL) wäre nett, ist aber 
viel zu früh, weil man nix über die Kosten weiß. CSE (common 
subextression elimination) wäre denkbar, aber in den nachfolgenden 
Passes wie Loop und Combine passiert zu viel als daß das in CSE schon 
abgeschätzt werden kann. Also irgendwann zwischen Combine und global 
Alloc? Und die Kosten-Frage ist immer noch nicht gelöst...

Johann

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Sebastian____ wrote:
> Hallo,
> ich habe ein kleines Problem. Bei einenm Projekt mit einem Mega8 habe
> ich den Flash fast voll.
> Aber zwischen den beiden Compilervarianten 20060421 <-> 20090313 erzeugt
> der WinAvr von 2006 einen 500byte kleineren code als der aktuelle
> WinAvr.
>
> Die optimierung ist jeweils schon auf "s" eingestellt.
> Aus dem Map file werde ich nicht richtig schlau, wie bekomme ich denn
> eine Ausflistung aus dem COmpiler welche funktion wie viel speicher im
> Controller belegt?
1
avr-nm --size-sort -S foo.elf | grep -i  ' t '

Ohne das grep bekommst du alle Objekte, also auch die Daten. Ich lass 
die aber immer separat anzeigen, also mit
1
avr-nm --size-sort -S foo.elf | grep -iv  ' t '

Johann

von Sebastian____ (Gast)


Lesenswert?

So ich habe das jetzt mal etwas weitergehen Analysiert:
1. #include <avr/eeprom.h>
im aktuellen Winavr wird eeprom_write_byte, eeprom_read_byte als 
funktion direkt eingebunden, im alten WinAvr tachen diese Funktionen 
nicht auf.

Die funktionen werden im Programm so aufgerufen:
1
uchar *pInfo= (uchar*)&savedKeys;
2
eeprom_write_block(pInfo, (uchar*)EE_SAVEKEYS, sizeof(savedKeys));
3
4
uchar *pInfo= (uchar*)&savedKeys;
5
eeprom_read_block(pInfo, (uchar*)EE_SAVEKEYS, sizeof(savedKeys));

2. Viele funktionen werden nur "etwas größer" in folgenden Beispiel 2 
byte von 16 auf 18
1
//ADC Wert Messen
2
uint getAdc(uchar adc_input){
3
4
  ADMUX=adc_input|ADC_VREF_TYPE;
5
  _delay_us(1);        //1us Pause für das stabilisierend der ADC Werte
6
  ADCSRA|=0x40;        // Start the AD conversion
7
  while ((ADCSRA & 0x10)==0);  // Wait for the AD conversion to complete
8
  ADCSRA|=0x10;
9
  return ADCW;
10
}

Mit dem Alten WINAVR wird daraus:
1
ADMUX=adc_input|ADC_VREF_TYPE;
2
     bb2:  87 b9         out  0x07, r24  ; 7
3
    __ticks = 1;
4
  else if (__tmp > 255)
5
    __ticks = 0;  /* i.e. 256 */
6
  else
7
    __ticks = (uint8_t)__tmp;
8
     bb4:  84 e0         ldi  r24, 0x04  ; 4
9
     bb6:  8a 95         dec  r24
10
     bb8:  f1 f7         brne  .-4        ; 0xbb6 <getAdc+0x4>
11
  _delay_us(1);    //1us Paus für das stabilisierend er ADC Werte
12
13
  // Start the AD conversion
14
  ADCSRA|=0x40;
15
     bba:  36 9a         sbi  0x06, 6  ; 6
16
  // Wait for the AD conversion to complete
17
  while ((ADCSRA & 0x10)==0);
18
     bbc:  34 9b         sbis  0x06, 4  ; 6
19
     bbe:  fe cf         rjmp  .-4        ; 0xbbc <getAdc+0xa>
20
  ADCSRA|=0x10;
21
     bc0:  34 9a         sbi  0x06, 4  ; 6
22
  return ADCW;
23
     bc2:  84 b1         in  r24, 0x04  ; 4
24
     bc4:  95 b1         in  r25, 0x05  ; 5
25
     bc6:  08 95         ret

Mit dem neuen WinAvr:
1
ADMUX=adc_input|ADC_VREF_TYPE;
2
     d86:  87 b9         out  0x07, r24  ; 7
3
    can be achieved.
4
*/
5
void
6
_delay_loop_1(uint8_t __count)
7
{
8
  __asm__ volatile (
9
     d88:  84 e0         ldi  r24, 0x04  ; 4
10
     d8a:  8a 95         dec  r24
11
     d8c:  f1 f7         brne  .-4        ; 0xd8a <getAdc+0x4>
12
  _delay_us(1);    //1us Paus für das stabilisierend er ADC Werte
13
14
  // Start the AD conversion
15
  ADCSRA|=0x40;
16
     d8e:  36 9a         sbi  0x06, 6  ; 6
17
  // Wait for the AD conversion to complete
18
  while ((ADCSRA & 0x10)==0);
19
     d90:  34 9b         sbis  0x06, 4  ; 6
20
     d92:  fe cf         rjmp  .-4        ; 0xd90 <getAdc+0xa>
21
  ADCSRA|=0x10;
22
     d94:  34 9a         sbi  0x06, 4  ; 6
23
  return ADCW;
24
     d96:  24 b1         in  r18, 0x04  ; 4
25
     d98:  35 b1         in  r19, 0x05  ; 5
26
}
27
     d9a:  c9 01         movw  r24, r18
28
     d9c:  08 95         ret

das Problem ist, das sich das so auf ein komplettes Programm mal 
schanell auf 500Byte aufsummiert. Meine Main schleife ist zb. von 
358byte auf 540byte angewachsen.

von (prx) A. K. (prx)


Lesenswert?

Das jetzt nicht wirklich dein Beispiel für Codeexplosion, oder? Exakt 
ein Befehl mehr.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

A. K. wrote:
> Das jetzt nicht wirklich dein Beispiel für Codeexplosion, oder? Exakt
> ein Befehl mehr.

Es ist viel Kleinvieh, und das macht eben auch Mist ;-)

Das sieht hier unschuldig aus, aber wenn es quer über die Anwendung hier 
und da ne Instruktion mehr gibt alle 10 Befehle, dann sind das schon 
rund 10% mehr Code. Und das ist echt satt; zumal mit dem Mehrcode noch 
nichtmal Laufzeit eingekauft wird, sondern stattdessen zur rumgetrödelt 
wird.

Naja, bei gcc 4 muss man schon froh sein, daß er bei (ADCSRA & 0x10) == 
0 nicht auf die Idee kommt, nen 16-Bit-Wert 4x nach rechts zu 
schieben... :o)

Wo in dem Beispiel der überflüssige Befehl herkommt ist nicht 
nachvollziehbar, dazu müsste man Compiler-Ausgaben sehen (zB mit -S 
-dp). Wahlscheinlich kommt der move von pass greg, das ist einer der 
kompliziertesten, vertracktesten und verhacktesten Teile von gcc. 
Momentan ne riesen Baustelle. In 4.4.0 gibt's ein neuen 
Register-Allokator. Bis dessen Kinderkrankheiten ausgewachsen sind, wird 
aber auch ein Weilchen dauern.

Sebastian____ wrote:
> So ich habe das jetzt mal etwas weitergehen Analysiert:
> 1. #include <avr/eeprom.h>
> im aktuellen Winavr wird eeprom_write_byte, eeprom_read_byte als
> funktion direkt eingebunden, im alten WinAvr tachen diese Funktionen
> nicht auf.
>
> Die funktionen werden im Programm so aufgerufen:
>
1
> uchar *pInfo= (uchar*)&savedKeys;
2
> eeprom_write_block(pInfo, (uchar*)EE_SAVEKEYS, sizeof(savedKeys));
3
> 
4
> uchar *pInfo= (uchar*)&savedKeys;
5
> eeprom_read_block(pInfo, (uchar*)EE_SAVEKEYS, sizeof(savedKeys));
6
>

Die ziehen die Prolog und Elipog-Funktionen nach sich, weil sie aus der 
mit -fcall-prologues übersetzten Lib kommen. Schau mal nach 
__elipogue_restores bzw. __prologue_saves oder so, sie bringen scon rund 
100 Byte mehr.

Johann

von Sebastian____ (Gast)


Lesenswert?

Ich glaub ich spinne, ich gehe gerade das list file des Codes duch.
im Code gibt es die funktion "CopyDefaultDataToRam();".
Diese wird mehrere male aufgerufen.
Und was macht der Compiler. Der kopiert die Funktion einfach 2x stumpf 
in das Listing. Der Alte WinAvr mach schön ein rcall auf die Funktion.
Ich habe erst mal ein paar Funktionen als "static" markiert, und siehe 
da, der Code ist schon mal 200byte kleiner geworden, nur durch doppeltes 
einlinken von Funktionen.

von (prx) A. K. (prx)


Lesenswert?

Sebastian____ wrote:

> Und was macht der Compiler. Der kopiert die Funktion einfach 2x stumpf
> in das Listing.

Meinst du inlining? Lässt sich verhindern. Siehe GCC Optionen, eine 
davon ist die schon erwähnte -fno-inline-small-functions. Eine andere 
Möglichkeit ist irgendein __attribute__.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Sebastian____ wrote:
> Ich glaub ich spinne, ich gehe gerade das list file des Codes duch.
> im Code gibt es die funktion "CopyDefaultDataToRam();".

Da wäre noch interessant, wie diese denn implementiert ist, dass
der GCC auf die Idee kommt, das Inlining könnte da was einsparen.

von (prx) A. K. (prx)


Lesenswert?

Die für's Inling verwendete Kalkulation vom GCC ist etwas fragwürdig.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

A. K. wrote:
> Die für's Inling verwendete Kalkulation vom GCC ist etwas fragwürdig.

Die Abkürzung A. K ist etwas fragwürdig.

Und Pauschalisierungen sind sowieso immer falsch. :-)

von Sebastian____ (Gast)


Lesenswert?

Hallo,
ich habe mal Testweise die funktion
CFLAGS += -fno-inline-small-functions
wieder rausgenommen.

bei
1
void CopyDefaultDataToRam(void){
2
  uchar *data;
3
  data= (uchar*)&settings;
4
  memcpy_P(data, &defaultSettings, sizeof(settings));
5
}

Die Funktion wird 2x in der Main aufgerufen.
1
#define noinline __attribute__((__noinline__)) //funktion nicht inline kompilieren
Wenn ich die funktion so definiere:
void CopyDefaultDataToRam(void), habe ich einen output von 5556bytes

Wenn ich die funktion so definiere:
noinline void CopyDefaultDataToRam(void), habe ich einen output von 
5532bytes.

Aktiviere ich im Makefile: CFLAGS += -fno-inline-small-functions
wird der Code auf 5528byes kleiner.
Als Vergleich, der Alte WINAVR Compiliert den Code auf 5134 Bytes.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Seltsam, dass der Unterschied so groß ist.  Die Funktion macht ja
weiter nichts, als die Parameter für ein memcpy_P() syntaktisch
aufzubereiten und dann memcpy_P() selbst zu rufen.  Wenn ich Compiler
wäre, hätte ich das vermutlich auch inline erweitert. ;-)

von Sebastian____ (Gast)


Lesenswert?

Wie behebt man die Problematik mit den EEprom Block-Copy routinen ohne 
mit ReadByte und WriteByte eine eigene BLock Copy Routine erstellen muß.
Die Problematik wurde hier: 
Beitrag "WinAVR 20090313 Released"
bereits angesprochen. Das eeprom_read_block/eeprom_write_block sehr viel 
Speicher verbrauchen.

Was passiert genau wenn man
CFLAGS += -mcall-prologues
in das Makefile einbindet? Was kann ich dann an funktionen nicht mehr 
nutzen? Eine Dokumentation dazu konnte ich noch nicht ausfindig machen.
Wenn man das im Makefile aktiviert, wird der Code recht genau 100byte 
kleiner.

von (prx) A. K. (prx)


Lesenswert?

Sebastian____ wrote:

> Was passiert genau wenn man
> CFLAGS += -mcall-prologues
> in das Makefile einbindet?

Der Overhead am Anfang und Ende einer Funktion wird kleiner, vor allem 
bei solchen mit vielen zu sicherenden Registern. Weil Aufrufe von 
Hilfsfunktionen statt Inline-Code verwendet werden. 
Performance-Einbussen dürften gering bis kaum nachweisbar sein. Hat 
sonst keine Auswirkungen.

von Benedikt K. (benedikt)


Lesenswert?

Es scheinen sich mittlerweile 2 Gruppen zu bilden:
Die einen bleiben bei den 2006/2007er Versionen, da sie keine Lust haben 
ihre Software und die makefiles anzupassen die andere updated immer auf 
die neueste Version.
Könnte vielleicht mal jemand, der wirklich Ahnung von den ganzen 
Compileroptionen hat, eine kurze Liste in dem 
AVR-GCC-Codeoptimierung Artikel erstellen, in der die wichtigsten 
Optionen und deren Vor und Nachteile/Risiken erläutert werden? Es 
scheint ja leider mittlerweile so zu sein, dass man einige Optionen 
nutzen muss, wenn der Compiler einen kompakten Code erzeugen sollte.

von Sebastian____ (Gast)


Lesenswert?

Für alte projekte verwende ich auch weiterhin die alten 
Compilerversionen, für neue Projekte versuche ich wenn möglich auch die 
aktuellen Compilerversionen zu nutzen.
Das kann man ja recht leicht per Virtuellem Ordner mit junction 
umschalten.

Ich bin jetzt mit den ganzen Optimierungen soweit das ich von 12% 
mehrcode auf 6% runter im Vergleich zu der alten WinAvr Version.

von Panzerknacker 176-671 (Gast)


Lesenswert?

>Das kann man ja recht leicht per Virtuellem Ordner mit junction
>umschalten.

Wie funktioniert das?

von Sebastian____ (Gast)


Lesenswert?

Bei Microsoft:
http://technet.microsoft.com/de-de/sysinternals/bb896768.aspx
gibt es das kleine Tool Junction.
Damit kann man Virtuelle Ordner Erstellen.

1. Alle WinAvr Versionen Installieren:
Winavr (alt) in C:\Programme\WinAvr installieren, dann den Ordner per 
Hand in C:\Programme\WinAVR-20060421 umbennen.
Winavr (neu) in C:\Programme\WinAvr installieren, dann den Ordner per 
Hand in C:\Programme\WinAVR-20090313 umbennen.

2. 2 Batch dateien zum Umschalten Anlegen:
WINAVR 2006
junction -d C:\Programme\WinAvr
junction  C:\Programme\WinAvr C:\Programme\WinAVR-20060421

WINAVR 2009
junction -d C:\Programme\WinAvr
junction  C:\Programme\WinAvr C:\Programme\WinAVR-20090313

3. je nach gewünschter Winavr Version einfach eine der beiden Batch 
Dateien ausführen um die entsprechende Compilerversion in den Pfad 
C:\Programme\WinAvr zu mappen.

von Guest (Gast)


Lesenswert?

Hallo,

kann mal jemand die Compileroption -mtiny-stack probieren? bei mir zeigt 
diese unter der 2009 Version keinerlei Wirkung mehr.

Danke.

von Panzerknacker 176-671 (Gast)


Lesenswert?

@Sebastian____ (Gast)

Vielen Dank

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Sebastian____ wrote:
> Bei Microsoft:
> gibt es das kleine Tool Junction.

Oder einfach den absoluten Pfad angeben und es hat sich der Lack...

A. K. wrote:
> Sebastian____ wrote:
>
>> Was passiert genau wenn man
>> CFLAGS += -mcall-prologues
>> in das Makefile einbindet?
>
> Der Overhead am Anfang und Ende einer Funktion wird kleiner, vor allem
> bei solchen mit vielen zu sicherenden Registern. Weil Aufrufe von
> Hilfsfunktionen statt Inline-Code verwendet werden.
> Performance-Einbussen dürften gering bis kaum nachweisbar sein. Hat
> sonst keine Auswirkungen.

räusper wenn ausser dem eeprom-Zeug keine Lib-Funktionen benötigt 
werden, dann schlägt -mcall-prologues mit rund 100 Bytes zu Buche. Für 
µC mit wenig Flash ist das sehr viel.

Jörg Wunsch wrote:
> Seltsam, dass der Unterschied so groß ist.  Die Funktion macht ja
> weiter nichts, als die Parameter für ein memcpy_P() syntaktisch
> aufzubereiten und dann memcpy_P() selbst zu rufen.  Wenn ich Compiler
> wäre, hätte ich das vermutlich auch inline erweitert. ;-)

memcpy_P ist ne Nicht-Standard Lib-Funktion und damit ne Blackbox. Wie 
will man ne Libfunktion inlinen? Für den Caller wird's aufwendiger, weil 
alle call-clobbered Register leergeräumt werden müssen, woher wohl der 
Mehrverbrauch resultiert. Für memcpy_P wär vielleicht ein builtin 
sinnvoller, bzw. die Funktion in die libgcc2 zu nehmen. Damit wären die 
Effekte explizit beschreibbar, ebenso wie bei den divmod und mul 
Funktionen, die als transparente Calls umgesetzt sind.

Johann

von (prx) A. K. (prx)


Lesenswert?

Johann L. wrote:

> räusper wenn ausser dem eeprom-Zeug keine Lib-Funktionen benötigt
> werden, dann schlägt -mcall-prologues mit rund 100 Bytes zu Buche. Für
> µC mit wenig Flash ist das sehr viel.

Ok, missverständlich formuliert. Im Platz macht es natürlich schon einen 
Unterschied. Je nach Programm in die eine oder andere Richtung.

Ich bezog mich da eher auf seine Frage "Was kann ich dann an funktionen 
nicht mehr nutzen?"

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Johann L. wrote:

>> Seltsam, dass der Unterschied so groß ist.  Die Funktion macht ja
>> weiter nichts, als die Parameter für ein memcpy_P() syntaktisch
>> aufzubereiten und dann memcpy_P() selbst zu rufen.  Wenn ich Compiler
>> wäre, hätte ich das vermutlich auch inline erweitert. ;-)
>
> memcpy_P ist ne Nicht-Standard Lib-Funktion und damit ne Blackbox. Wie
> will man ne Libfunktion inlinen?

Es ging ja darum, dass die Funktion CopyDefaultDataToRam inline
erweitert worden ist.  Da diese (bis auf die Bereitstellung der
Parameter) praktisch einen direkten Aufruf von mempcy_P() als
einziges verbleibendes Element enthält, verstehe ich, dass der
Compiler zu dem Schluss kommt, dass man sie inline erweitern sollte.
Was ich nur nicht verstehe ist, dass daraus eine massive Vergrößerung
des Codes entstehen soll.

von (prx) A. K. (prx)


Lesenswert?

Ist memcpy_P eine "echte" Funktion, oder selber wiederum inlined?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

A. K. wrote:
> Ist memcpy_P eine "echte" Funktion, oder selber wiederum inlined?

memcpy_P gehört -- wie gesagt -- zur avr-libc. Und gegen eine Lib kann 
man nur linken. Für memcpy_P ist dem Compiler erstens der Code nicht 
bekannt, und zweitens ist er in asm:

http://cvs.savannah.gnu.org/viewvc/avr-libc/libc/pmstring/memcpy_P.S?revision=1.8&root=avr-libc&view=markup

Johann

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch wrote:
> Johann L. wrote:
>> memcpy_P ist ne Nicht-Standard Lib-Funktion und damit ne Blackbox. Wie
>> will man ne Libfunktion inlinen?
>
> Es ging ja darum, dass die Funktion CopyDefaultDataToRam inline
> erweitert worden ist.  Da diese (bis auf die Bereitstellung der
> Parameter) praktisch einen direkten Aufruf von mempcy_P() als
> einziges verbleibendes Element enthält, verstehe ich, dass der
> Compiler zu dem Schluss kommt, dass man sie inline erweitern sollte.
> Was ich nur nicht verstehe ist, dass daraus eine massive Vergrößerung
> des Codes entstehen soll.

Ah, ok. Es gibt immer auch Grenzfälle. Wenn zum Beispiel nur ein 
Register mehr gebraucht wird, das gcc aber nicht mehr hat, dann legt er 
für die Funktion nen Frame an. Und dann wird's richtig teuer. Im Prolog, 
im Epilog und bei den Zugriffen auf die Variablen, die im Frame leben.

Davon ab geschieht im Beispiel das Laden der Symbole in der Funktion, 
das kostet schon ein paar Bytes. Klarheit würde aber nur die 
Compiler-Ausgabe(n) bringen.

Johann

von Stefan E. (sternst)


Lesenswert?

Johann L. wrote:
> A. K. wrote:
>> Ist memcpy_P eine "echte" Funktion, oder selber wiederum inlined?
>
> memcpy_P gehört -- wie gesagt -- zur avr-libc. Und gegen eine Lib kann
> man nur linken.

Das ist doch kein Argument gegen mögliches Inlining. Die Funktion könnte 
schließlich in einer Header-Datei definiert sein, wie einige andere 
Funktionen der AVR-Libc auch.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Stefan Ernst wrote:

> Das ist doch kein Argument gegen mögliches Inlining. Die Funktion könnte
> schließlich in einer Header-Datei definiert sein, wie einige andere
> Funktionen der AVR-Libc auch.

Ist sie aber nicht, sondern sie ist eine echte Biblitoheksfunktion,
die für den Compiler erst einmal eine Blackbox ist.  Johann hat doch
den Link auf den Quelltext schließlich schon zitiert, und du kannst
seiner Einschätzung über die Wirkmechanismen gut und gern blind über
den Weg trauen, wenn du's selbst nicht besser weißt.  Ich habe von
ihm schon GCC-RTL-Code gesehen...

von Stefan E. (sternst)


Lesenswert?

Ich weiß, dass es eine echte Bibliotheksfunktion ist. Das habe ich ja 
auch nicht in Frage gestellt, sondern seine Begründung.

A. K.:
Echte Funktion oder Inlining?

Johann L.:
memcpy_P gehört zur avr-libc, also kein Inlining.

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.