AVR-GCC pointer post-increment

#7459345
Lesenswert?

Hi,

Ich habe im Microchip Studio ein Projekt für ATXMEGA64A1U erstellt. GCC, bzw Toolchain scheint Version 5.4.0 zu sein.

Folgender c/c++ code (optim. -Os):

1
Temp = pgm_read_byte(ptr++);

Für nicht dazu, dass der compiler

1
LPM rX,Z+

benutzt.

Gleiches gilt komischerweise auch für pointer im normalen SRAM space. Zumindest kenne ich *ptr++ also normale C schreibweise für post-increment.

Gibt es da mittlerweile ein built-in oder sowas, sodass man nicht eigene asm routinen schreiben muss? Gab ja mal einen Beitrag, wo jmd einen workaround gepostet hat. Gab sogar mal ein Bug Report bei GCC selbst, allerdings für mich etwas zu cryptisch. Den Report poste ich später, ist aber auch über AVR GCC Bug-Liste zu finden.

VG

#7459430
Lesenswert?

Wim M. schrieb:

Für nicht dazu, dass der compiler

1
LPM rX,Z+

benutzt.

Gleiches gilt komischerweise auch für pointer im normalen SRAM space.

Das kann im Sinne der Optimierung eben auch durchaus die richtige Entscheidung sein. Insbesondere bezüglich des SRAM-Zugriffs. Bei Optimierung auf Speed könnte eine abgerollte Schleife tatsächlich (u.U. sogar deutlich) effzienter sein, wenn sie mit Index+Offset zugreift. Das kann man aber nur dann beurteilen, wenn Schleifen-Count zur Compilezeit bekannt und >64 ist. Spätestens dann sollte der Compiler auf jeden Fall zu der Addressierungsart mit Postincrement umschwenken.

Was den Flash-Zugriff angeht dürfte es sich aber um eine fehlende Optimierung handeln, denn da geht kein Index+Offset. Ist kein Bug, nur eben suboptimal. Aber heh: ist Hochsprache, das ist eher selten wirklich optimal...

Von diesen Compiler-Mankos lebe ich doch. Gäbe es die nicht zu Unmassen, würde Assemblerprogrammierung ja keinen Spass mehr machen ;o)

#7459576
Lesenswert?

Wim M. schrieb:

Gibt es da mittlerweile ein built-in oder sowas, sodass man nicht eigene asm routinen schreiben muss?

So wie ich das sehe, ist pgm_read_byte() ein Macro, das bereits Inline Assembler benutzt. Der Compiler kann das gar nicht optimieren, weil er den Assembler-Quelltext aus dem Macro weitgehend unbesehen einfach in seinen Assembler-Output einbaut (in der Phase ist er mit seinen eigenen Optimierungen längst durch).

Wenn das also ein Fehler sein soll (sehe ich nicht), dann höchstens in der avr-libc.

#7459672
Lesenswert?

Markus F. schrieb:

in der Phase ist er mit seinen eigenen Optimierungen längst durch

Das ist so nicht richtig. GCC asm statements werden in der Optimierung mitbedacht. Aber natürlich nur soweit es die Schnittstellendefinition, Constraints und Clobbers erlauben.

Das lpm in ein postinc-lpm umzuformen kann er aber tatsächlich nicht. Die Optimierung schaut nicht in den Asm-Code selbst rein. Nur in die Schnittstellenbeschreibung.

#7459715
Lesenswert?

MaWin O. schrieb:

Das ist so nicht richtig.

Haarspalterei. Alles, was der gcc hier noch tun kann, ist z.B. ein Ergebnis verwerfen, wenn es unbenutzt erscheint oder das Inline-Assembly aus einer Schleife herausziehen, falls das möglich ist. "Echte" Optimierungen finden da nicht mehr statt.

Aber auch das passiert hier nicht, weil das aus dem Macro expandierte Inline-Assembly in pgmspace.h volatile spezifiziert ist.

#7459726
Lesenswert?

MaWin O. schrieb:

Das lpm in ein postinc-lpm umzuformen kann er aber tatsächlich nicht. Die Optimierung schaut nicht in den Asm-Code selbst rein. Nur in die Schnittstellenbeschreibung.

Ja. Er verändert den Code nicht, sondern lässt ihn höchstens komplett weg, wenn er an der Stelle laut Schnittstellenbeschreibung keine Auswirkung auf das Programm hat - und wenn er nicht als volatile definiert ist.

Markus F. schrieb:

So wie ich das sehe, ist pgm_read_byte() ein Macro, das bereits Inline Assembler benutzt.

Ja.

Moderator Persönliche Seite #7459813
Lesenswert?

Rolf M. schrieb:

ist pgm_read_byte() ein Macro

Ungeachtet vom geschilderten Problem:

Man sollte bei Macros auf Argumente mit gleichzeitigem Pre-/Post-Increment generell verzichten. Wird ein Argument im Macro tatsächlich mehrfach genutzt, wird auch mehrfach inkrementiert. Das will man nicht.

Beispiel:

1
#include <stdio.h>
2

3
#define CHECKSUM(x,y)  ((x) * (x) + (y))
4

5
int main()
6
{
7
    int a = 2;
8
    int b = 1;
9
    int c = CHECKSUM(a++,b);
10

11
    printf ("%d\n", c);
12
    return 0;
13
}

Rätselfrage: Was wird ausgegeben? Und warum?

Da man als Programmierer nicht immer nachschauen möchte, ob man da nun ein Makro oder eine Funktion aufruft, habe ich es mir prinzipiell abgewöhnt, Pre-/Post-Increment in Parametern zu verwenden. Das war vor vielen Jahren noch eine manuelle Anweisung an den damaligen dummen C-Compiler, hier zu optimieren. Das ist aber heute nicht mehr notwendig und damit auch obsolet.

#7459839
Lesenswert?

Frank M. schrieb:

Rolf M. schrieb:

ist pgm_read_byte() ein Macro

Ähm, das habe nicht ich geschrieben.

Ungeachtet vom geschilderten Problem:

Man sollte bei Macros auf Argumente mit gleichzeitigem Pre-/Post-Increment generell verzichten. Wird ein Argument im Macro tatsächlich mehrfach genutzt, wird auch mehrfach inkrementiert. Das will man nicht.

Das ist bei diesem Makro aber nicht der Fall.

Rätselfrage: Was wird ausgegeben?

Der Sinn des Lebens - oder gar nichts - oder 42. Alles korrekt.

Und warum?

Weil wegen UB.

Frank M. schrieb:

Ingo L. schrieb:

7, wegen des Postincrements (2) * (3) + 1

Korrekt :)

Ja, aber jede beliebige andere Antwort wäre genauso korrekt.

Da man als Programmierer nicht immer nachschauen möchte, ob man da nun ein Makro oder eine Funktion aufruft, habe ich es mir prinzipiell abgewöhnt, Pre-/Post-Increment in Parametern zu verwenden.

Die Konvention ist, dass Makros durch GROSSSCHREIBUNG als solche gekennzeichnet werden. Vielleicht hat man das in diesem Fall bewusst nicht gemacht, weil es hier kein Problem darstellt.

#7459855
Lesenswert?

Ingo L. schrieb:

Rolf M. schrieb:

Ja, aber jede beliebige andere Antwort wäre genauso korrekt.

Warum?

Weil das Verhalten undefinert ist, wenn man eine Variable ohne dazwischen liegenden Sequenzpunkt mehr als einmal ändert. Und undefinertes Verhalten heißt, dass beliebiges passieren darf. (a++) * (a++) ändert a zweimal, ohne dass ein Sequenzpunkt dazwischen ist.

Moderator Persönliche Seite #7459860
Lesenswert?

Rolf M. schrieb:

Die Konvention ist, dass Makros durch GROSSSCHREIBUNG als solche gekennzeichnet werden.

Leider wird zu oft diese Konvention nicht eingehalten. Von daher würde ich es nicht darauf ankommen lassen. Es ist sogar so, dass mit Hilfe von Conditional Compilation von der libc oft zu Funktionen alternative Makros aus Performancegründen angeboten werden, siehe z.B. ctype.h.

Vielleicht hat man das in diesem Fall bewusst nicht gemacht, weil es hier kein Problem darstellt.

Das ist lediglich eine Vermutung. Ich würde nicht drauf wetten.

#7459910
Lesenswert?

Eine Frage vorweg:

Sind auf diesem Forum eigentlich 99% der Trolle Deutschlands unterwegs oder einfach nur schlecht gelaunt? Verglichen zu englisch-sprachigen Foren sind deu. Foren auf ner Skala von 1-10 leider 12...und zwar toxisch. Ahnung scheint ihr ja aber auch nicht zu haben, oder teilt sie nicht, weil der Stolz ebenso bei 12 ist. Wie halten seriöse Menschen das hier auf dem Forum nur aus?

Zurück zum Thema:

Danke and MaWin:

MaWin O. schrieb:

Das lpm in ein postinc-lpm umzuformen kann er aber tatsächlich nicht. Die Optimierung schaut nicht in den Asm-Code selbst rein. Nur in die Schnittstellenbeschreibung.

Ja genau, deshalb ja kein "Z+". Deswegen alternative zu Marko oder eigener ASM.

Danke auch an den Ein oder Anderen für eine vernünftige Antwort! Die restlichen Antworten haben leider nichts mit meiner Frage zu tun.

Die Alternative habe ich jetzt auch gefunden: __flash mit -std-gnu99. Hier ein Beispielcode, der "Z+" mit Optimierung -Os erzeugt:

1
#include <avr/io.h>
2
#include <avr/pgmspace.h>
3

4
const char __flash string[16] = "Hello world!";
5

6
int main(void)
7
{
8
  volatile char foo;
9
  __flash const char* ptr = string;
10
  
11
  for (uint16_t i= 0; i < 16; ++i)
12
  {
13
    foo = *(ptr)++;
14
    // oder:
15
    // foo = string[i]; // (erzeugt gleichen asm)
16
  }
17
  
18
  
19
    while (1);
20
}

Allerdings programmiere ich lieber mit einem C++ Projekt. Dort müsste ich das dann in extern "C" packen? Nur "checkt" der Compiler "__flash" irgendwie nicht, obwohl der C-Compiler -std-gnu99 gesetzt hat. Was hab ich vergessen?:

1
extern "C"
2
{
3
  const char __flash string[16] = "Hello World!";
4
};

Comp. Error: "expected initializer before 'string'" Wenn ich __flash an eine andere Stelle setzte (davor): Error: "'__flash' does not name a type"

Wen der Thread zum Compiler-"Bug" interessiert: (geht aber nicht primär um 'lpm') siehe https://www.mikrocontroller.net/articles/Avr-gcc_Bugs siehe https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81611

VG

EDIT: achso ja, die Post-Increment-Pointer für SRAM wurden mit sicherheit wegoptimiert. Im obigen Beispiel optimiert der Compiler "Z+" bei for-Schleife "i<5" auch weg.

#7459942
Lesenswert?

Wim M. schrieb:

Sind auf diesem Forum eigentlich 99% der Trolle Deutschlands unterwegs

Das dachte ich als erstes, als ich deinen Nicknamen laß.

Zum Thema: Du könntest ein eigenes kleines C-Modul mit den Flashzugriffen schreiben, welches du dann zum C++ Rest dazulinkst.

Ich hatte ja oben schon gefragt, weshalb es dir auf eine Instruktion ankommt. Scheint aber ein großes Geheimnis zu sein? Solch Mikrooptimierung ist in der Regel "Unsinn" und hilft im ernsthaften Fall nur sehr selten weiter. Die Fehler liegen meistens woanders.

#7460441
Lesenswert?

Oliver S. schrieb:

Der vom Compiler erzeugte Code benötigt unfaßbare 1 word flash und irrsinnige 1 cycle Ausführungszeit mehr als mit postinkrement.

Es sind zwei Zyklen. Siehe ISRM->adiw

Und natürlich fällte der Zusatzaufwand in jeder Iteration einer Schleife an, was wohl in der überwiegenden Zahl der Fälle für Flashzugriffe der Normalfall sein dürfte. Und dann summiert sich der Mehrbedarf leicht merklich auf.

Noch störender ist das bei SRAM-Zugriffen, denn auf neueren AVR8 kosten die in vielen ADressierungsarten nur noch einen Takt. Sprich: der Overhead durch die unnütze Zusatzinstruktion beträgt dann satte 200% des Aufwands für den Nutzcode. Beim Flash-Zugriff (3 Takte) sind's "nur" 67%.

#7460496
Lesenswert?

C-hater schrieb:

Sprich: der Overhead durch die unnütze Zusatzinstruktion beträgt dann satte 200% des Aufwands für den Nutzcode.

Unter der Voraussetzung, dass du die Werte nur liest und dann verwirfst, ohne was damit zu machen. Sobald du sie auch nutzt, wird sich der Prozentsatz mehr oder weniger weit, aber in der Regel sehr deutlich, verringern.

Moderator Persönliche Seite #7460665
Lesenswert?

Wim M. schrieb:

Nur "checkt" der Compiler "__flash" irgendwie nicht, obwohl der C-Compiler -std-gnu99 gesetzt hat. Was hab ich vergessen?:

Dass es __flash bei C++ nicht gibt.

Wim M. schrieb:

Für nicht dazu, dass der compiler

LPM rX,Z+

benutzt.

Das liegt einfach daran, dass dein pgm_read_byte() Inline-Assembler-Code ist, bei dem der Compiler zwar die Parameter optimieren kann, aber nicht die Assemblerbefehle selbst.

#7460913
Lesenswert?

MaWin O. schrieb:

Kommt halt immer darauf an. Wenn das Programm funktioniert, warum dann optimieren? Um nun 50% statt 60% der CPU-Leistung zu nutzen?

Das ist nicht der Punkt. Der Punkt ist:

Wenn das Programm nicht optimiert halt 105% der verfügbaren Rechenleistung benötigt, optimiert aber nur 95%.

Dann mach das nämlich den Unterschied zwischen "geht" und "geht nicht" aus.

Capisce, stupido?

#7460922
Lesenswert?

Jörg W. schrieb:

Dass es __flash bei C++ nicht gibt.

Warum eigentlich nicht?

__flash ist zwar auch noch nicht das "Gelbe vom Ei", aber doch immerhin schon deutlich besser als die pgm-Geschichte.

Besser übrigens nicht nur bezüglich der möglichen Optimierungen, sondern auch rein syntaktisch "logischer". Natürlich nur soweit man bei C/C++ überhaupt von einer Logik der Syntax sprechen kann...

#7460923
Lesenswert?

C-hater schrieb:

Oliver S. schrieb:

Da widersprechen sich die verschiedenen Versionen des Instruction Set manuals

Ist das so? Ich habe noch keins gesehen, in dem adiw mit einem Takt spezifiziert gewesen wäre.

Kannst du mal ein Beispiel zeigen?

https://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf

S33/34

Oliver

#7460940
Lesenswert?

Hi,

Oliver S. schrieb:

Wim M. schrieb:

Nur "checkt" der Compiler "__flash" irgendwie nicht, obwohl der C-Compiler -std-gnu99 gesetzt hat. Was hab ich vergessen?:

__flash kennt nur der C-Compiler, aber nicht der C++-Compiler. Auch weil du da extern C hinschriebst, wird die Zeile nicht mit dem C-Compiler compiliert.

Oliver

Ja dann muss ich mir das mal anschauen, wofür EXTERN genau da ist, bzw. wie man halt C in C++ implementiert. Danke soweit.

Für mich ist das Thema erledigt damit.

VG

#7460976
Lesenswert?

Oliver S. schrieb:

https://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf

Interessantes Teil. Wo hast du denn das her?

In den Changes taucht nicht einmal adiw/sbiw auf. D.h.: das Dokument meinte offiziell auch in allen Vorfahren, dass adiw/sbiw nur einen Takt benötigt.

Dem ist aber nachweislich nicht so.

Also: du hast wohl die einzige fehlerhafte Spezifikation aus 20 Jahren rausgefischt. Und zwar sehr wahrscheinlich eine, die offiziell niemals hätte zugänglich sein dürfen. Denn sonst hätte die entsprechende Korrektur wiederum in der nächsten Version erscheinen müssen...

Das tat sie aber nicht.

MC hat sich die Sache jetzt übrigens viel einfacher gemacht. Nachvollziehbare Changes gibt es einfach nicht mehr...

Rotz, wie er von MC nicht anders zu erwarten war...

#7460989
Lesenswert?

C-hater schrieb:

Also: du hast wohl die einzige fehlerhafte Spezifikation aus 20 Jahren rausgefischt. Und zwar sehr wahrscheinlich eine, die offiziell niemals hätte zugänglich sein dürfen.

Nicht ich, sondern Google. Das war halt die nöchstbeste, die ich „mal eben schnell“ zum nachschauen aufgemacht habe. Wo die herkommt, keine Ahnung.

Oliver

#7460990
Lesenswert?

Oliver S. schrieb:

Ist das so? Ich habe noch keins gesehen, in dem adiw mit einem Takt spezifiziert gewesen wäre.

Kannst du mal ein Beispiel zeigen?

https://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf

Das ist offenbar ein Tippfehler. Im selben Dokument auf Seite 22 sind dafür 2 Taktzyklen angegeben. Für SBIW sind an beiden Stellen 2 Zyklen angegeben.

#7461016
Lesenswert?

Rolf M. schrieb:

Unter der Voraussetzung, dass du die Werte nur liest und dann verwirfst, ohne was damit zu machen. Sobald du sie auch nutzt, wird sich der Prozentsatz mehr oder weniger weit, aber in der Regel sehr deutlich, verringern.

Natürlich. Es bleibt aber: Es ist (mehr oder weniger deutlich) suboptimal.

Nur für Leute, die ernsthaft glauben, ein Compiler macht alles optimal, eine kaum glaubhafte Überraschung...

Ich denke mal, du gehörst genau so wenig zu diesem Personenkreis wie Oliver S. Warum, zum Teufel fällt es euch dann so schwer zu sagen: Ja, das geht besser? Und zu sagen: Ja, in Asm kann man es besser machen?

Wenn jeder der kompetenten Leute bereit wäre, diese offensichtliche Wahrheit auch genauso offen zuzugeben, wäre diese ewige Diskussion doch praktisch beendet.

Die wird immer nur wieder von den Bibeltreuen angeheizt, die eben immer wieder und gegen jede Vernunft und gegen offensichtliche Fakten unverbrüchlich und wiederholt behaupten: Der Compiler macht das schon alles perfekt. Nein, das tut er eben nicht.

#7461023
Lesenswert?

C-hater schrieb:

Nur für Leute, die ernsthaft glauben, ein Compiler macht alles optimal,

Niemand hat das jemals geglaubt.

Und zu sagen: Ja, in Asm kann man es besser machen

Das war jedem hier im Thread vom Anfang an klar. Das Thema des Threads ist aber AVR-GCC, nicht AVR-ASM.

Wenn jeder der kompetenten Leute bereit wäre, diese offensichtliche Wahrheit auch genauso offen zuzugeben, wäre diese ewige Diskussion doch praktisch beendet.

Nein, ganz und gar nicht. Asm statt C zu nehmen löst kein einziges Problem des C-Optimierers. Es ist lediglich ein Workaround.

Beitrag #7461029 wurde von einem Moderator gelöscht.
Beitrag #7461034 wurde von einem Moderator gelöscht.
#7461069
Lesenswert?

C-hater schrieb:

Ich denke mal, du gehörst genau so wenig zu diesem Personenkreis wie Oliver S. Warum, zum Teufel fällt es euch dann so schwer zu sagen: Ja, das geht besser? Und zu sagen: Ja, in Asm kann man es besser machen?

Da du mich ja direkt ansprichst, also dann: ja, man kann da in ASM 2 Zyklen sparen. Bestreitet niemand.

Eine sicherlich typische Anwendung wäre jetzt z.B eine CRC-Berechnung übers ganze Flash. Was bei den 64kB des Prozessors, um den es hier geht, dann 128k verschwendete Zyklen ergibt. Bei schnarchlangsamen 1Mhz sind das 0.13s, bei 16Mhz halt 1/16 davon.

Wenn das für eine Anwendung funktionsentscheidend ist, muß man das in ASM basteln, wenn nicht, dann nicht.

Dafür musst due jetzt hier wirklich nicht den Moby machen.

Oliver

#7461368
Lesenswert?

Handgeschriebenen (in diesem Fall Inline-, aber nichtsdestotrotz handgeschriebenen), suboptimalen Assembler-Code als Beweis für die unanfechtbare Überlegenheit von Assembler gegenüber Compiler-generiertem Code zu verargumentieren, entbehrt nicht einer gewissen Komik, ehrlich.

Ja, man kann mit Assembler oft den besseren Code schreiben. Wenn man's kann.

Man kann das aber (offensichtlich) auch so verbissen sehen, dass man den Wald vor Bäumen nicht mehr erkennt.

#7462768
Lesenswert?

Cyblord -. schrieb:

MaWin O. schrieb:

Und dabei wissen wir doch alle, dass sowohl C als auch Asm auf den Müllhaufen der Geschichte gehören.

Man könnte so weit gehen, C, C++ und ASM als Vogelschiss der Geschichte zu bezeichnen.

Dem würde ich mich anschliessen. Das Problem ist eben einfach nur, dass es Targets gibt, deren Resourcen für den Overhead wirklich brauchbarer Programmierprachen unzureichend sind.

Und die programmiert man in C/C++, wenn das Speichermodell dieser Sprachen wenigstens annähernd passt, ansonsten halt sinnvollerweise eher doch in Assembler. Was halt bei den AVR8 mit Harvard-Architektur, mehreren Adressräumen, diversen Mappings(incl. Banking) zwischen diesen Adressräumen die einzige logische Entscheidung eines wirklich kompetenten Programmierers sein kann.

#7462773
Lesenswert?

MaWin O. schrieb:

C-hater schrieb:

Du hältst also __flash für der Weisheit letzten Schluß?

hä?

Was ist dein Problem? Hast du noch nie was von __flash gehört oder sind dir nur die Grenzen nicht bewußt?

Hinweis: Das ist (in erster Näherung für Blöde) einfach ein anderes "API" für den Zugriff auf Daten im Flash. Eine Alternative zu dem elenden "progmem"-Zeugs.

#7462795
Lesenswert?

C-hater schrieb:

Hast du noch nie was von __flash gehört

Ja doch. Aber wer soll denn behauptet haben

Du hältst also __flash für der Weisheit letzten Schluß?

?

Niemand hat auch nur annähernd sowas gesagt. Das basiert nur auf deinem Beißreflex

far away from to be perfect.

Ja, wir wissen ja, dass nur Asm perfekt sein kann, nach deiner Definition.

#7462804
Lesenswert?

Falk B. schrieb:

C-hater schrieb:

immer noch: far away from to be perfect.

Done is better than perfect!

No way. Done perfectly without the need to use such strange workarounds is perfect. I did it. Many times.

And you could do so. If you want. You only must want to be perfect and learn how to to it and proceed...

The joke is: in many times it is much more simple to do it in in asm than in C/C++. Especially of course for "C-inkompatible" targets like AVR8.

#7462814
Lesenswert?

C-hater schrieb:

Nicht nach meiner Definition, sondern nach den Grungesetzen der Informatik. Das ist ein himmelweiter Unterschied, der dir aber anscheinend nicht wirklich klar ist. (Aber klar sein sollte!)

Die Grundgesetze der Logik sagen mir, dass ein Compiler, der optimalen Asm-Code ausgibt, optimal ist. Welches Grundgesetz der Informatik dagegen sprechen sollte, bleibt wohl dein Geheimnis.

#7462833
Lesenswert?

MaWin O. schrieb:

Die Grundgesetze der Logik sagen mir, dass ein Compiler, der optimalen Asm-Code ausgibt, optimal ist.

Neeeiiiin.

Optimal wäre er, wenn er das immer und unter allen Umständen tun würde. Davon ist aber jeder existierende Compiler sehr weit weg. Der avr-gcc natürlich auch. Allerdings (das muss man wohl auch mal sagen): nicht viel weiter als die kommerzielle Konkurrenz, manchmal ist er sogar näher dran am Optimum.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren