Hallo,
ich arbeite derzeit mit dem sdcc und nicht dem gcc aber ich frage mich,
was man einem Compiler zumuten kann. Der sdcc ist ein 1 Pass Compiler.
Der Ausdruck
len = strlen(keybuf);
if ((len < 5) || (len > 21))
return 0;
ist gleich
len = strlen(keybuf);
if ((len < strlen(keybuf)) || (strlen(keybuf) > 21))
return 0;
Der sdcc erzeugt daraus zweimal einen Aufruf nach strlen, samt der
Beladung der Register für den Aufruf. Vesion 1 ist einiges kürzer als
Nr. 2
Wie wäre denn nun ein guter Coding Style? Schauen was man zusammenfassen
kann oder das dem Compiler überlassen, dass der clever genug ist zu
erkennen, dass ein Ausdruck mehrfach verwendet wird?
Grüße,
Christian
Christian J. schrieb:> Der sdcc erzeugt daraus zweimal einen Aufruf nach strlen, samt der> Beladung der Register für den Aufruf. Vesion 1 ist einiges kürzer als> Nr. 2
woher soll denn der Compiler wissen, das strlen reentrant ist?
strlen könnte ja auch den string manipulieren und jeweils 1 Zeichen dran
hängen.
Wenn man 2 Funktionsaufrufe hinschreibt, wird die Funktion auch 2 mal
aufgerufen.
Numn ja,
wenn ich zweimal
y = x*z;
benutze, dann ist der CCS Compiler schon so clever zu merken, dass der
Ausdruck zwischengespeichert werden muss. Der CCS ist allerdings für
PIC.
Beim sdcc für Z80 merke ich jedenfalls, dass es etrem was bringt, wenn
man mehrfach vorkommende Ausdrücke vorher berechnet. Mehr Variablen =
weniger Code. Den ultimativen Boost für die Codesize bringt es alle
Variablen auf static zu setzen, so dass der Stack frei bleibt.
Christian J. schrieb:> wenn ich zweimal>> y = x*z;>> benutze, dann ist der CCS Compiler schon so clever zu merken, dass der> Ausdruck zwischengespeichert werden muss.
das ist etwas anders. Hier geht es nur um variablen und da darf er alles
machen so lange am ende das richtige rauskommt.
1
y=strlen(x);
2
y=strlen(x);
3
y=strlen(x);
4
y=strlen(x);
sollte auch nicht optimiert werden, eventuell bei C++ weil der Parameter
dort const ist.
Peter II schrieb:> woher soll denn der Compiler wissen, das strlen reentrant ist?
Was hat das mit „reentrant“ zu tun?
Ein Compiler im hosted environment darf jedoch in der Tat davon
ausgehen, dass die Funktion strlen() genau das tut, was im Standard
steht. Lediglich im freestanding environment ist sie wie eine
unbekannte Funktion zu behandeln.
Vermutlich beherrscht der SDCC allerdings diese Unterscheidung und
die daraus möglichen Optimierungen sowieso nicht. In diesem Falle
kann man dem Compiler natürlich schon mal aushelfen und die erste
Variante wählen.
Peter II schrieb:> eventuell bei C++ weil der Parameter dort const ist.
Auch bei C ist er seit C89 const. Das sagt allerdings noch nichts
darüber aus, dass das Ergebnis dieser Funktion nur vom übergebenen
Argument abhängt (es könnte noch von globalen Variablen oder einem
von vorherigen Aufrufen gespeicherten Zustand abhängen).
Im Falle von strlen() jedoch ist der Fall auch in C eindeutig
optimierbar, solange (siehe voriges Posting) ein hosted environment
vorliegt.
Jörg Wunsch schrieb:> Ein Compiler im hosted environment darf jedoch in der Tat davon> ausgehen, dass die Funktion strlen() genau das tut, was im Standard> steht. Lediglich im freestanding environment ist sie wie eine> unbekannte Funktion zu behandeln.
ich dachte das hosted environment nur dinge sind wofür man auch kein
include braucht z.b. sizeof. strlen wird doch in der libc implementiert
und kann dinge tun von dem der Compiler nicht weiß.
Peter II schrieb:> strlen wird doch in der libc implementiert und kann dinge tun von dem> der Compiler nicht weiß.
Was glaubst du wohl, warum es einen C-Standard gibt und warum dies
eben die Standardbibliothek ist?
Peter II schrieb:> strlen wird doch in der libc implementiert> und kann dinge tun von dem der Compiler nicht weiß.
der GCC kennt dafür "attributes", die man der Funktion mitgeben kann.
in dem Fall "pure" für strlen:
1
Many functions have no effects except the return value and their return
2
value depends only on the parameters and/or global variables. Such a
3
function can be subject to common subexpression elimination and loop
4
optimization just as an arithmetic operator would be. These functions
5
should be declared with the attribute pure. For example,
6
7
8
int square (int) __attribute__ ((pure));
9
10
says that the hypothetical function square is safe to call fewer times
11
than the program says.
12
13
Some of common examples of pure functions are strlen or memcmp.
14
Interesting non-pure functions are functions with infinite loops or those
15
depending on volatile memory or other system resource, that may change
16
between two consecutive calls (such as feof in a multithreading
17
environment).
18
19
The attribute pure is not implemented in GCC versions earlier than 2.96.
Jörg Wunsch schrieb:> Was glaubst du wohl, warum es einen C-Standard gibt und warum dies> eben die Standardbibliothek ist?
dann müsste müsst die libc aber immer fest mit dem Compiler verbunden
sein.
die Erklärung von Linksammler finde ich logischer, das es ein extra
Attribut gibt. Damit könnte man das auch bei eigenen Funktionen
einsetzen.
Peter II schrieb:> die Erklärung von Linksammler finde ich logischer, das es ein extra> Attribut gibt. Damit könnte man das auch bei eigenen Funktionen> einsetzen.
Die erklärt aber nicht, wie der compiler aus einem "strlen"-Aufruf mit
Konstantem Parameter gleich eine Konstante (Zahl) macht.
GCC kennt die Eigenschaften der eingebauten Funktion __builtin_strlen(),
auf die irgendwo in den Includes die Funktion strlen() umgesetzt wird.
Damit ist volle Optimierung möglich. Das kann ein Compiler machen, er
muss es nicht.
Wenn man andererseits auf
#include <string.h>
verzichtet und
extern int strlen(const char *);
hinschreibt, dann hat man den Aufruf 4x drin.
Teilt man GCC mit, dass die Funktion nur von ihren Parametern abhängt
und keine Seiteneffekte hat, dann hat man genau einen Aufruf:
extern int strlen(const char *) __attribute__((pure));
A. K. schrieb:> GCC kennt die Eigenschaften der eingebauten Funktion __builtin_strlen(),> auf die irgendwo in den Includes die Funktion strlen() umgesetzt wird.> Damit ist volle Optimierung möglich. Das kann ein Compiler machen, er> muss es nicht.
danke, das erklärt es nun.
Damit kennt der Compiler also nicht wirklich strlen sondern nur
__builtin_strlen und in den includes wird darauf gemappt.
Peter II schrieb:> dann müsste müsst die libc aber immer fest mit dem Compiler verbunden> sein.
Nein. Eine C-Standard-Bibliothek muss sich halt immer nur so
verhalten, dass sie die im Standard beschriebenen Funktionen auch
exakt so implementiert, wie es der Standard vorsieht.
Ein typisches Beispiel ist ja die avr-libc: sie wird als Projekt
unabhängig vom GCC implementiert, aber für all die Funktionen, die
sie implementiert(*), hält sie sich an den Standard. Der Compiler
wiederum geht im hosted Mode (-fhosted, Default beim GCC) genau
davon aus, und darf (das gestattet ihm der Standard) daher auch
internes Wissen darum haben und ausnutzen, wie sich eine
standardkonforme Funktion verhält.
(*) und die im Standard vorgesehen sind – Erweiterungen sind natürlich
etwas anderes
Peter II schrieb:> Damit kennt der Compiler also nicht wirklich strlen sondern nur> __builtin_strlen und in den includes wird darauf gemappt.
Nein. Nochmal das gleiche Spiel mit dem AVR-GCC, einfach nur durch
den Präprozessor geschickt:
Also kein __builtin_strlen, dennoch wird der Aufruf durch eine
Konstante ersetzt:
1
.file "hw.c"
2
__SP_H__ = 0x3e
3
__SP_L__ = 0x3d
4
__SREG__ = 0x3f
5
__tmp_reg__ = 0
6
__zero_reg__ = 1
7
.text
8
.global main
9
.type main, @function
10
main:
11
/* prologue: function */
12
/* frame size = 0 */
13
/* stack size = 0 */
14
.L__stack_usage = 0
15
ldi r24,lo8(12)
16
ldi r25,0
17
ret
18
.size main, .-main
19
.ident "GCC: (GNU) 4.7.2"
Optimierung mehrfacher Aufrufe wäre noch mit dem "pure"-Attribut zu
erklären, das Ersetzen durch eine Konstante jedoch nicht.
Zum Gegenvergleich, nochmal mit -ffreestanding:
Es gibt Compiler-Techniken, mit denen ein Compiler selbst herausfinden
kann, ob eine Funktion Nebeneffekte hat, auch wenn eine Funktion in
einem anderen Quellfile implementiert ist. Letztlich wird dabei in den
einzelnen Compilerläufen der Code nur vorübersetzt, die Codegenerierung
und Optimierung findet erst im Linker statt.
Im GCC dürfte das unter LTO = "link time optimization" zu finden sein,
bei Microsoft wohl unter "Whole Program Optimization".
A. K. schrieb:> Im GCC dürfte das unter LTO = "link time optimization" zu finden sein
Ja, aber selbst die schlägt in meinem Beispiel ja nicht zu, da gar
nicht gelinkt worden ist. Ist das reine Compilat, wie es sich nach
einem Aufruf mit -S ergibt.
Jörg Wunsch schrieb:> Ja, aber selbst die schlägt in meinem Beispiel ja nicht zu, da gar> nicht gelinkt worden ist.
Betrifft auch eher eigene Funktionen. Hatte nur als Ergänzung zum Thema
aufgeführt, nicht als Erklärung von strlen().
Peter II schrieb:> Jörg Wunsch schrieb:>> Ein Compiler im hosted environment darf jedoch in der Tat davon>> ausgehen, dass die Funktion strlen() genau das tut, was im Standard>> steht. Lediglich im freestanding environment ist sie wie eine>> unbekannte Funktion zu behandeln.>> ich dachte das hosted environment nur dinge sind wofür man auch kein> include braucht z.b. sizeof.
Nein, das gibt es immer, egal ob hosted oder nicht.
Der Hauptunterschied zwischen einem hosted und eiem freestanding
environment ist, daß ersteres das Vorhandensein der Standardbibliothek
voraussetzt, letzteres nicht. sizeof ist kein Teil der
Standardbibliothek.
> strlen wird doch in der libc implementiert und kann dinge tun von dem> der Compiler nicht weiß.
Was strlen macht, ist bei einem hosted environment genau festgelegt.
Wenn die libc was anderes macht, ist sie nicht konform. Der Compiler
kann also auch wenn er nicht mit der libc "verheiratet" ist, gewisse
Annahmen über das Verhalten treffen, unter anderem auch, daß sie bei
mehrmaligem Aufruf mit dem selben String auch das selbe Ergebnis
zurückliefert. Dazu sind an sich auch keine speziellen Attribute
notwendig, sondern es reicht, daß es sich um ein hosted environment
handelt und die Funktion strlen() heißt.
Peter II schrieb:> dann müsste müsst die libc aber immer fest mit dem Compiler verbunden> sein.
Für die Optimierung hilft es, wenn sie sich gegenseitig kennen. Tun sie
meist auch. Beim gcc geht das dann noch einen Schritt weiter, indem er
gleich selber einige Funktionen der libc implementiert. So kann er daher
auch bei Strings, die er zur Compilezeit kennt, den Aufruf der Funktion
strlen() komplett wegoptimieren und das Ergebnis gleich als Konstante in
den code schreiben. gcc macht sowas heute für große Teile der libc.
Siehe folgende Liste:
https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html> die Erklärung von Linksammler finde ich logischer, das es ein extra> Attribut gibt. Damit könnte man das auch bei eigenen Funktionen> einsetzen.
Kann man auch. Trotzdem gibt es für den Compiler noch einige weitere
Freiheiten bei der Implementation der Standardbibliotheken.
Peter II schrieb:> A. K. schrieb:>> GCC kennt die Eigenschaften der eingebauten Funktion __builtin_strlen(),>> auf die irgendwo in den Includes die Funktion strlen() umgesetzt wird.>> Damit ist volle Optimierung möglich. Das kann ein Compiler machen, er>> muss es nicht.>> danke, das erklärt es nun.>> Damit kennt der Compiler also nicht wirklich strlen sondern nur> __builtin_strlen und in den includes wird darauf gemappt.
Im Prinzip ja, wobei das eine Design-Entscheidung des
Compiler-Herstellers ist. Der könnte sich auch dazu entscheiden, die
gesamte Standardbibliothek im Compiler selbst zu implementieren und
Standardheader gar nicht als Dateien auszuführen.
Ein
1
#include<stdio.h>
wäre dann nur ein spezielle Marker im Programm, der daraufhin die
entsprechenden Bezeichner der Standardbibliothek quasi "freischaltet".
ich kann mir gut vorstellen, dass der compiler pure-functions mit
compiletime-konstantem parameter selbst ausführen kann. bei
Math-functions macht er das doch auch... da wird ein ln(1) einfach durch
0 im quellcode ersetzt... dort wundert sich aber keiner.. warum?
was für mich interessant ist wäre:
>const char* huhu[strlen(huhu)] = "huhu";
compiliert das?
... schrieb:> ich kann mir gut vorstellen, dass der compiler pure-functions mit> compiletime-konstantem parameter selbst ausführen kann.
Wenn sie inline sind, sollte er das in vielen Fällen hinbekommen. Dazu
müssen sie dann aber nicht mal pure sein. Das ist ja nur in dem Fall
relevant, in dem der Compiler die Implementation nicht kennt. Da kann er
aber die Funktion nicht selber ausführen. Er kann nur bestimmte Annahmen
darüber treffen und z.B. mehrfaches Ausführen mit den selben
Parameterwerten verhindern.
> bei Math-functions macht er das doch auch... da wird ein ln(1) einfach> durch 0 im quellcode ersetzt... dort wundert sich aber keiner.. warum?
ln ist bereits im Compiler selbst implementiert.
> was für mich interessant ist wäre:>>>const char* huhu[strlen(huhu)] = "huhu";>> compiliert das?
Hier mußt du unterscheiden. Nur weil der Compiler etwas zur Compilezeit
ausrechnen kann, heißt das nicht, daß er es im Quelltext wie eine
Konstante behandeln darf. Die Optimierung ändert nichts an der Validität
des Code. strlen ist eine Funktion, die aufgerufen werden muß und zur
Laufzeit ein Ergebnis zurückgibt, also kann man sie nicht dort
verwenden, wo eine Konstante notwendig ist. Daß das Ergebnis durch
Optimierungen bereits zur Compilezeit berechnet werden kann, spielt
dabei keine Rolle.
Ähm... ok. :-)
Meine Frage war aber schon beantwortet. Der sdcc kann es nicht, auch
nicht bei fixen Strings im Quelltext. Er ruft immer brav auf und
Optmierung ist etwas was man selbst machen muss.
Christian J. schrieb:> ist gleich
Wo wird im zweiten Codeschnippsel geprüft, ob der String weniger als 5
Zeichen hat?
Und sollte der Parameter aus irgendeinem Grund volatile sein, so kann
bei mehrmaligem Aufruf der Funktion tatsächlich ein anderes Ergebnis
rauskommen.