Forum: Compiler & IDEs Kann man denn jetz das hi- und low-byte eines uint16_t effizient lesen in c?


von Leo B. (luigi)


Lesenswert?

Servus zusammen,

ich stehe vor einem klein Problem. Und zwar hab ich für ein Uniprojekt 
einen großen kleinen Fehler gemacht, indem ich statt den Großen die 
kleinen AVRs ausgewählt habe... Nunja jetzt sitze ich hier vor meinem 
ATtiny24 mit nur 2kb Speicher und bekomme das Programm um ein paar Byte 
nicht in den Speicher.
Nun habe ich herausgefunden, dass offenbar vor allem das lesen der 
hi-Bytes einiger uint16_t-Variablen recht verschwenderisch ist.
( Momentan mache ich das mit: hi = (uint8_t)(word>>8); )
Jetzt habe ich natürlich schon das große G bemüht, doch leider erschlägt 
mich dieses mit tausenden Forenbeiträgen, die sich zwar ums gleiche 
Problem drehen, aber leider keine eindeutigen Lösungen bieten.
Da jetzt in meinem speziellen Fall auch noch erschwerend hinzu kommt, 
dass ich im Bereich der Hardwarenahen und vor allem Hardware sparsamen 
Programmierung sehr neu bin, fehlt mir hier ein wenig Know-how.

Nach allem was ich gelesen habe scheint ja eine union wie folgende recht 
geeignet zu sein.
1
typedef union
2
{
3
  uint16_t i;
4
  uint8_t b[2];
5
} u16_t;
allerdings frage ich mich wie ich dabei sicher herausfinde welches das 
high- und welches das low-byte ist...
Oder gibt es alternative Ansätze die effizienter sind? inline assembler 
vielleicht? ich kann damit kaum umgehen aber eine Lösung muss her!

Ich bitte euch zudem mir nicht zum kauf neuer ICs zu raten. Dass dies 
die einfachste Lösung wäre ist mir klar, allerdings werde ich dann dann 
von meinem Betreuer gekillt, auch wenn sie nur sehr wenig kosten...

Ich danke euch schonmal für eure Unterstützung
Gruß
Leo

von B. S. (bestucki)


Lesenswert?

> ATtiny24 mit nur 2kb Speicher und bekomme das Programm um ein paar Byte
> nicht in den Speicher.
Ich kenn die AVRs nicht, aber vielleicht gibts da ja was ähnliches. Beim 
PIC gibt es ein Linker-File, in dem die Speicherbereiche aufgeteilt 
sind. Dort ist es manchmal möglich, noch einige ungenutzte Bytes 
herauszuquetschen.

> Momentan mache ich das mit: hi = (uint8_t)(word>>8);
So hätt ich das auch gemacht.

> Oder gibt es alternative Ansätze die effizienter sind? inline assembler
> vielleicht? ich kann damit kaum umgehen aber eine Lösung muss her!
Schau mal was der Compiler draus macht. Evt. ist es gar nicht möglich, 
dieses Problem effizienter zu lösen. Dann brauchst du dich auch nicht 
weiter darum zu kümmern.

Hast du beim Compiler die Optimierung eingeschalten?

Evt. ist es möglich, die Speicheradresse der Variablen manuell zu 
definieren. Dann könntest du mit Assembler das höhere Byte direkt 
auslesen.

von Fabian O. (xfr)


Lesenswert?

Der Compiler ist schon so schlau, dass er den Shift um 8 Stellen als 
Zugriff aus der High-Byte erkennt. Wie Du es jetzt machst, ist schon 
genau richtig. Du musst aber natürlich die Compiler-Optimierung 
einschalten (Optimize for Size, -Os). Wenn Du Zweifel daran hast, schau 
Dir den Assembler-Code, den der Compiler ezeugt, an.

Die Methode mit der Union ist Murks, eben weil je nach Prozessorfamilie 
die Bytereihenfolge (Endianness) unterschiedlich sein kann.

von Fabian O. (xfr)


Lesenswert?

Und nur mal auf Verdacht hin: Verwendest Du Fließkommazahlen? Das ist 
nämlich der Speicherfresser schlechthin bei Code von Anfängern.

von blubb (Gast)


Lesenswert?

Kannst ja mal einen beliebigen Ausschnitt aus dem Code posten und 
eventuell sieht man gleich eine Optimierungsmöglichkeit... wenn es nur 
um ein paar Bytes geht...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Leo B. schrieb:

> Nun habe ich herausgefunden, dass offenbar vor allem das lesen der
> hi-Bytes einiger uint16_t-Variablen recht verschwenderisch ist.
> ( Momentan mache ich das mit: hi = (uint8_t)(word>>8); )

Ein Testfall wäre hilfreich.

avr-gcc 4.7 compiliert mit -Os folgenden Testfall
 
1
#include <stdint.h>
2
3
uint8_t get_hi (uint16_t word)
4
{
5
    uint8_t hi = (uint8_t)(word>>8);
6
    return hi;
7
}
 
zu:
 
1
get_hi:
2
  mov r24,r25
3
  ret
 
Da ist nix mehr zu optimieren...

> typedef union
> {
>   uint16_t i;
>   uint8_t b[2];
> } u16_t;

Dazu sagt C99 6.2.6.1 #7:

>> When a value is stored in a member of an object of union type,
>> the bytes of the object representation that do not correspond
>> to that member but do correspond to other members take unspecified
>> values,

Konkret: Wenn dein Programm in C stehen soll, kannst du keine Union 
verwenden, um die einzelnen Bytes zu bekommen.

>> note that this is a GCC extension which might not work with
>> other compilers

Bedeutet: In GCC wird obiges Konstrukt — bzw. was du damit anstellen 
willst — unterstützt, siehe

http://gcc.gnu.org/bugs/#nonbugs_c


> Oder gibt es alternative Ansätze die effizienter sind? inline
> assembler vielleicht? ich kann damit kaum umgehen aber eine
> Lösung muss her!

Auch Inline-Assembler ist kein C.

Vermutlich gibt es viele andere Stellen im Code, wo gespart werden kann.

von Walter (Gast)


Lesenswert?

Leo B. schrieb:
> Ich bitte euch zudem mir nicht zum kauf neuer ICs zu raten. Dass dies
> die einfachste Lösung wäre ist mir klar, allerdings werde ich dann dann
> von meinem Betreuer gekillt, auch wenn sie nur sehr wenig kosten...

ich kriege z.B. die tiny85 billiger als die tiny25,
ist vielleicht bei tiny24 auch so ...

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Poste doch mal dein Programm, da gibtz sicher noch was zu optimieren :-)
Oder nimm mal den IAR Compiler (Demo), vielleicht kriegt der das 
kleiner.

von Peter D. (peda)


Lesenswert?

Leo B. schrieb:
> Nun habe ich herausgefunden, dass offenbar vor allem das lesen der
> hi-Bytes einiger uint16_t-Variablen recht verschwenderisch ist.

Das halte ich für ein Gerücht.
Laß Dir das List-File erzeugen und schau nach, wo viel Code entsteht.

Während der Entwicklung könnte man aber auch einfach erstmal nen 
ATtiny84 einstecken.


Peter

von Leo B. (luigi)


Angehängte Dateien:

Lesenswert?

Danke erst einmal, für eure Hilfe!
Den ganzen Code werde ich jetzt mal nicht posten. Zum Einen ist der 
nicht sehr sauber geschrieben in Bezug auf Kommentierung und 
Formatierung, zum anderen müsst ihr nicht meine Arbeit machen. Aber 
danke trotzdem.
Ich nutze jetzt den avr-gcc 4.7.2 (vorher 4.3.3) und compiliere mit -Os:
mein test.c - File:
1
#include <stdint.h>
2
#include <avr/io.h>
3
4
typedef struct
5
{
6
        uint8_t lo;
7
        uint8_t hi;
8
} hilobyte_t;
9
10
typedef union
11
{
12
        uint16_t i;
13
        hilobyte_t b;
14
} u16_t;
15
16
17
volatile u16_t uni;
18
volatile uint8_t u8;
19
volatile uint16_t u16;
20
21
int main( void )
22
{
23
24
  uni.i = ADC;
25
  u16 = ADC;
26
  u8 = PINA;
27
28
  while(1)
29
  {
30
    
31
    if( uni.b.hi < 128 )
32
      continue;
33
    if( (volatile uint8_t)(u16>>8) < 128 )
34
      continue;
35
    if( *((volatile uint8_t*)(&u16)+1) < 128 )
36
      continue;
37
    if( u8 < 128 )
38
      continue;
39
    
40
    asm("NOP"); // damit das letzte if-Statement nicht weg optimiert wird
41
  }
42
43
  return 0;
44
}
nach dem Compilieren kommt das Angehängte .lss-file raus.
So wie ich das deute ist alles gut nur die shift-operation nicht... Da 
will er ums verrecken den ganzen volatile uint16 laden. vermute es liegt 
am volatile, den ich aber zwingend brauche. Und gerade die Methode mit 
dem shiften ist die einzige, welche mir sicher das high-byte liefert. 
Das ist schon doof.

von Karl H. (kbuchegg)


Lesenswert?

Leo B. schrieb:
> Danke erst einmal, für eure Hilfe!
> Den ganzen Code werde ich jetzt mal nicht posten.

Das wirst du aber müssen.

Vergiss die Idee mit dem Hibyte-LowByte. Das ist nicht dein Problem.
Dein Problem sind Codefresser ganz anderer Natur. Die können wir dir 
aber nicht suchen helfen, wenn wir nicht deinen richtigen Code sehen.


Wenn das Ausheben einer Baugrube zu lange dauert, dann bringt es nichts 
an der Optimierung des Sandkastenschäufelchens zu feilen. Klotzen, nicht 
kleckern!

> Zum Einen ist der
> nicht sehr sauber geschrieben in Bezug auf Kommentierung und
> Formatierung,

Die Kommentierung ist mir ehrlich gesagt wurscht. Die ignoriere ich 
sowieso, weil sie in den meisten Fällen nichtssagend ist und nicht 
weiter hilft. Und in Punkto Formatierung solltest du das schleunigst IN 
DEINEM EIGENEN INTERESSE bereinigen. Ein sauber geschriebenes Programm 
ist nicht einfach nur Selbstzweck. Eine ordentliche Formatierung hilft 
auch bei der Fehlersuche und Fehlervermeidung.

von Peter II (Gast)


Lesenswert?

Leo B. schrieb:
> asm("NOP"); // damit das letzte if-Statement nicht weg optimiert wird

warum sollte soetwas passieren?

das Problem könnte aber mit dem volatile zusammenhängen. Du solltest die 
Variabel einmal laden und dann ohne volatile verwenden.

In diesem Beispiel macht das volatile eh keinen sinn, hier kannst du es 
weglassen.

von Karl H. (kbuchegg)


Lesenswert?

> vermute es liegt am volatile,

logisch.
Genau das ist ja der Sinn von volatile: Keine Optimiertricks erlaubt. 
Jeder Zugriff hat so zu erfolgen, wie er im Code steht. Und in deinem 
Code steht nun mal, dass eine 16-Bit Variable gelesen werden soll. Also 
wird auch genau das gemacht.

> den ich aber zwingend brauche

Auch da gibts unter Umständen Möglichkeiten.

Zeig deinen richtigen Code, sonst hampeln wir am Montag immer noch rum.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Leo B. schrieb:
> Den ganzen Code werde ich jetzt mal nicht posten.

Das ist schlecht.

Ich nehme auch an, dass es am volatile liegt. Die Methode, das in den 
Griff zu bekommen, ist, das volatile in eine lokale Variable 
umzuspeichern, mit dieser alle Operationen, die notwendig sind, 
durchzuführen und das Ergebnis (falls erforderlich!) wieder 
zurückzuspeichern.

Aber ohne den realen Code wird das nichts - nur Stochern im Nebel.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Und dann das:
1
   while(1)
2
   {
3
       ....
4
       if( u8 < 128 )
5
          continue;
6
    
7
       asm("NOP"); // damit das letzte if-Statement nicht weg optimiert wird
8
   }

Was soll der Quatsch? Klar kann der Compiler das wegoptimieren:

1. Wenn u8 < 128 ---> continue
2. Anderenfalls am Ende der while-Schleife ----> AUCH continue!

Dieses if-Statement ist total überflüssig - nicht nur für den Compiler, 
sondern auch für den gesunden Menschenverstand.

Das gleiche gilt für alle anderen if-Statements in der while-Schleife. 
Die können ALLE WEG, DENN SIE TUN NICHTS! Du versuchst das mit volatile 
auszugleichen: kompletter Unsinn.

Wenn der restliche Code ähnlich aussieht, nehme ich mal an, dass man den 
vom Compiler erzeugten Code auf 50% verkleinern kann - wenn man es 
richtig macht.

von MaWin (Gast)


Lesenswert?

> allerdings frage ich mich wie ich dabei sicher herausfinde
> welches das high- und welches das low-byte ist...

In dem du ein Testprogramm schreibst.

Es hängt nämlich davon ab, auf welcher Art von Prozessor das C-Programm 
läuft.

Auf Prozessoren von INtel mit low Endian wo das niederwertigste Byte 
zuerst kommt, oder auf Prozessoren bei denen das höher wertige Byte 
zuerst kommt.

Es ist also Prozessorabhängig, ändert sich aber auf der Plattform nicht 
von byte zu byte.

Dann hast du schon einen effizienten Zuriff auf Bytes eines Worts. Ob 
das allerdings schneller ist als ein (vom Compiler optimierter) Zugriff 
>>8, ist dann ebenfalls plattformabhängig (und hängt damit vom Compiler 
ab).

Vielleicht verbrät sein Programm ja Platz woanders. Beispielsweise 
unnötig funktionslokale Variablen, mach sie global, das Spart Platz, auf 
uC muss man mnachmal anders denken.

von Peter II (Gast)


Lesenswert?

MaWin schrieb:
> Beispielsweise
> unnötig funktionslokale Variablen, mach sie global, das Spart Platz, auf
> uC muss man mnachmal anders denken.

genau das Gegenteil. lokale Variabel können im Register bleiben, globale 
müssen immer aus und in den Ram geladen werden.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Du solltest doch mal das ganze Prog posten, ich bin sicher, das kriegen 
wir noch auf unter 1kByte geschrumpft :-))

von Oliver S. (oliverso)


Lesenswert?

MaWin schrieb:
> Beispielsweise
> unnötig funktionslokale Variablen, mach sie global, das Spart Platz,

Hm. ???

Ich würde eher den oben gegebenen Tipps folgen, und per .lss und 
.map-File anaylsieren, welche Programmteile wieviel Platz benötigen, und 
dann fallweise optimieren. Geniale 
Super-Duper-Rundum-Sorglos-Optimierungstricks sind häufig urban legends 
aus der Compiler-Steinzeit.

Oliver

von Peter D. (peda)


Lesenswert?

Leo B. schrieb:
> Da
> will er ums verrecken den ganzen volatile uint16 laden.

Er macht nicht, was er will, sondern was Du ihm mit dem volatile 
befiehlst.
Das Listing ist vollkommen korrekt.
Nur was Du als C-Code hingeschrieben hast, ist Mumpitz.
Wenn der Rest auch so aussieht, na dann gute Nacht.

Volatile nach volatile zu casten, ist auch Unsinn.


Peter

von Karl H. (kbuchegg)


Lesenswert?

Oliver S. schrieb:
> Geniale
> Super-Duper-Rundum-Sorglos-Optimierungstricks sind häufig urban legends
> aus der Compiler-Steinzeit.

Das kann man nicht laut genug sagen!

Programmoptimierung beginnt nicht damit, dass man einzelne Anweisung 
durch andere Anweisungen ersetzt und dann plötzlich alles wundersamer 
Weise um 50% schneller, besser, kleiner ist. Programmoptimierung beginnt 
mit dem großen Bild des Codes ... auf algorithmischer Ebene.
Den Kleinkram überlässt man dem Compiler. Selbst dann, wenn der manchmal 
nicht ganz so optimiert, wie man sich das vorstellt. Im großen und 
ganzen sind das dann Peanuts, die in 99% aller Fälle nicht weiter ins 
Gewicht fallen. Und nein, mit seinem eigenen Programm ist man nicht bei 
diesem 1% dabei. Wer in dieser Kategorie operiert, der weiß das und muss 
solche Fragen nicht mehr in einem Forum stellen.

Anders ausgedrückt: Egal wie gut ich einen Bubble-Sort optimiere, die 
Performance eines Quick-Sort werde ich damit nie erreichen. Selbst dann 
nicht, wenn der Quick-Sort schlecht geschrieben ist.
Und mit Codegrößen ist es ähnlich.

von MaWin (Gast)


Lesenswert?

> genau das Gegenteil.

Nein. Probier's aus.

> lokale Variabel können im Register bleiben,
> globale müssen immer aus und in den Ram geladen werden.

Nur wenn volatile dransteht.

von Peter II (Gast)


Lesenswert?

MaWin schrieb:
>> genau das Gegenteil.
> Nein. Probier's aus.
mach ich ständig ich verwende sie immer wenn sie nicht global sein 
müssen

>> lokale Variabel können im Register bleiben,
>> globale müssen immer aus und in den Ram geladen werden.
>
> Nur wenn volatile dransteht.
jain, wenn sie volatile dran steht dann ist es immer der Fall, wenn es 
nicht dran steht dann macht er es aber auch sehr oft. Spätestens in 
einer ISR werden sie IMMER gelaaden und gespeichert.

Dann brauche globale Variable auch noch platz im flash. Also nur 
nachteile gegenüber lokalen Variablen.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Johann L. schrieb:
>>> the bytes of the object representation that do not correspond
>>> to that member but do correspond to other members take unspecified
>>> values,
>
> Konkret: Wenn dein Programm in C stehen soll, kannst du keine Union
> verwenden, um die einzelnen Bytes zu bekommen.

Das hat hier kürzlich schon mal jemand nicht richtig verstanden.

Dieser Abschnitt beschreibt das Verhalten von Unions bei Verwendung 
unterschiedlich großer Elemente, also z.B. das Mischen von char und int.

Das aber ist hier nicht der Fall, hier sind beide Elemente gleich groß.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Rufus Τ. Firefly schrieb:
> Johann L. schrieb:
>>>> the bytes of the object representation that do not correspond
>>>> to that member but do correspond to other members take unspecified
>>>> values,
>>
>> Konkret: Wenn dein Programm in C stehen soll, kannst du keine Union
>> verwenden, um die einzelnen Bytes zu bekommen.
>
> Das hat hier kürzlich schon mal jemand nicht richtig verstanden.
>
> Dieser Abschnitt beschreibt das Verhalten von Unions bei Verwendung
> unterschiedlich großer Elemente, also z.B. das Mischen von char und int.
>
> Das aber ist hier nicht der Fall, hier sind beide Elemente gleich groß.

Gut, denn steht auf der GCC-Webseite http://gcc.gnu.org/bugs/#nonbugs_c 
offenbar totaler Schrott.

von Bernhard M. (boregard)


Lesenswert?

Johann L. schrieb:
>> typedef union
>> {
>>   uint16_t i;
>>   uint8_t b[2];
>> } u16_t;
>
> Dazu sagt C99 6.2.6.1 #7:
>
>>> When a value is stored in a member of an object of union type,
>>> the bytes of the object representation that do not correspond
>>> to that member but do correspond to other members take unspecified
>>> values,
>
> Konkret: Wenn dein Programm in C stehen soll, kannst du keine Union
> verwenden, um die einzelnen Bytes zu bekommen.

Wenn man das kombiniert mit 6.5, #7:
 7 An object shall have its stored value accessed only by an lvalue
   expression that has one of the following types: {footnote 73}
   ...
     a character type.



Wenn ich das richtig verstehe, ist ein (der einzige?) Weg, der gehen 
muß:
1
uint16_t i;
2
char *b;
3
char byte1, byte2;
4
5
b = &i;
6
7
byte1 = b[0];
8
byte2 = b[1];
wobei natürlich die endianness bestimmt, wo high und lowbyte hinkommen.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Johann L. schrieb:
> Gut, denn steht auf der GCC-Webseite http://gcc.gnu.org/bugs/#nonbugs_c
> offenbar totaler Schrott.

Magst Du versuchen, mir zu zeigen, was exakt Du meinst? Das da 
aufgeführte Beispiel der Verwendung einer union beschreibt, wie ich auch 
erwähnte, den Effekt der Verwendung zweier gleichgroßer Elemente der 
Union, die sich erwartungsgemäß verhalten. Und nicht andersherum.

Entweder verstehe ich immer noch nicht, was Du versuchst zu sagen, oder 
unser englisches Textverständnis unterscheidet sich deutlich.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Rufus Τ. Firefly schrieb:
> Johann L. schrieb:
>> Gut, denn steht auf der GCC-Webseite http://gcc.gnu.org/bugs/#nonbugs_c
>> offenbar totaler Schrott.
>
> Magst Du versuchen, mir zu zeigen, was exakt Du meinst? Das da
> aufgeführte Beispiel der Verwendung einer union beschreibt, wie ich auch
> erwähnte, den Effekt der Verwendung zweier gleichgroßer Elemente der
> Union, die sich erwartungsgemäß verhalten. Und nicht andersherum.


So wie ich C99 6.2.6.1 #7 verstehet, werden durch Schreiben einer 
Komponente X einer Union alle anderen Komponenten ungültig -- unabhängig 
von den Aliasing-Regeln, derer sich andere Klauseln annehmen:

1) X wird geschrieben
2) Y wird gelesen.

Der Compiler geht nun hin und analysiert den Daten- und Codefluss.  Kann 
der Compiler beim Lesen von Y nachweisen, daß das letzte geschriebene 
Element nicht Y war (was hier der Fall sein soll), dann darf angenommen 
werden, daß Y keinen spezifizierten Inhalt hat.  Und zwar unabhängig von 
seinem Typ und von seiner Größe und den Aliasing-Regeln.

> Auf der GCC-Seite steht:
>
> To fix the code above, you can use a union instead of a cast
> (note that this is a GCC extension which might not work with
> other compilers):
> #include <stdio.h>
>
> int main()
> {
>   union
>   {
>     short a[2];
>     int i;
>   } u;
>
>   u.a[0]=0x1111;
>   u.a[1]=0x1111;
>
>   u.i = 0x22222222;
>
>   printf("%x %x\n", u.a[0], u.a[1]);
>   return 0;
> }
>
> Now the result will always be "2222 2222".

Dies ist also eine GCC-Erweiterung; man beachte das "might not work with
other compilers".  Und im darauffolgenden Link steht:

> There are cases where you wish to access the same memory as
> different types:
>
>     float *f = 2.718;
>     printf("The memory word has value 0x%08x\n", *((int*)f));
>
> You cannot do that in ISO C, but gcc has an extension in that
> it considers memory in unions as having multiple types, so the
> following will work in gcc (but is not guaranteed to work in
> other compilers!)
>
>     union {
>         int i;
>         float f;
>     } u;
>     u.f = 2.718;
>     printf("The memory word has value 0x%08x\n", u.i);
>
> One bieffect of this is that gcc may miss optimization
> opportunities when you use union-heavy constructs.

Dies wird also in GCC unterstützt, aber nicht unbedingt in anderen 
Compilern.

In GCC funktioniert das übrigens unabhängig von -f[no-]strict-aliasing, 
wodurch Optimierungen aufgrund von Aliasing ab- bzw. anschaltet werden.

Der einzig standardkonforme Weg, die Bits von einem float zu bekommen, 
ist damit memcpy (mir fällt jedenfalls kein anderer Weg ein ausser 
Inline Assembler, und der ist kein Standard):
1
#include <stdint.h>
2
#include <string.h>
3
4
uint32_t fbits (float f)
5
{
6
    uint32_t b;
7
    memcpy (&b, &f, 4);
8
    return b;
9
}

von Rolf Magnus (Gast)


Lesenswert?

Johann L. schrieb:
> Der einzig standardkonforme Weg, die Bits von einem float zu bekommen,
> ist damit memcpy

Das ist auch der einzige in C99 explizit erwähnte Weg, und es ist 
übrigens auch ein sehr effizienter, zumindest bei GCC, da memcpy dort 
bereits vom Compiler selbst implementiert und sehr gut optimiert ist.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Johann L. schrieb:
> So wie ich C99 6.2.6.1 #7 verstehe

Das würde implizieren, daß Unions ein ziemlich nutzloses Sprachfeature 
sind. Ob das die Absicht ist?

von Rolf Magnus (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Johann L. schrieb:
>> So wie ich C99 6.2.6.1 #7 verstehe
>
> Das würde implizieren, daß Unions ein ziemlich nutzloses Sprachfeature
> sind. Ob das die Absicht ist?

Nein, es impliziert, daß unions eigentlich für was ganz anderes gedacht 
sind.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Wir drehen uns hier im Kreis.

> When a value is stored in a member of an object of union type,
> the bytes of the object representation that do not correspond
> to that member but do correspond to other members take unspecified
> values,

Offensichtlich haben wir sehr unterschiedliche Auffassungen, wie das zu 
übersetzen ist.

> Wenn ein Wert in einem Element einer Union gespeichert wird, erhalten
> die Bytes der Union, die nicht zu diesem Element, aber zu anderen
> Elementen gehören, undefinierte Werte.

(Hervorhebung von mir)

von Rolf Magnus (Gast)


Lesenswert?

Letztendlich findet sich in Anhang J.1 "Unspecified behavior" noch:

"The following are unspecified:
[...]
— The value of a union member other than the last one stored into 
(6.2.6.1)."

In 6.5.2.3/5 wird noch eine einzige Ausnahme genannt:

"One special guarantee is made in order to simplify the use of unions: 
if a union contains several structures that share a common initial 
sequence (see below), and if the union object currently contains one of 
these structures, it is permitted to inspect the common initial part of 
any of them anywhere that a declaration of the complete type of the 
union is visible."

Wenn ich also mehrere Strukturen in der union habe, die alle exakt 
gleich anfangen, dann darf ich diesen Anfang auch über das falsche 
Member nutzen.

von Rolf Magnus (Gast)


Lesenswert?

Ach so, was ich vergessen habe:

Rufus Τ. Firefly schrieb:
>> Wenn ein Wert in einem Element einer Union gespeichert wird, erhalten
>> die Bytes der Union, die nicht zu diesem Element, aber zu anderen
>> Elementen gehören, undefinierte Werte.
>
> (Hervorhebung von mir)

Das stimmt schon. Es bezieht sich nur auf die zusätzlichen Bytes, wenn 
man ein Member speichert, das einen kleineren Typ hat. Es sagt erstmal 
noch gar nichts darüber aus, was passiert, wenn man ein anderes als das 
zuletzt geschriebene Element liest.

von Fabian O. (xfr)


Lesenswert?

Rufus Τ. Firefly schrieb:
>> When a value is stored in a member of an object of union type,
>> the bytes of the object representation that do not correspond
>> to that member but do correspond to other members take unspecified
>> values

Das heißt aus meiner Sicht einfach nur, dass man in einer Union (im 
Gegensatz zu einem Struct) nur eines der Elemente beschreiben darf, wenn 
man das Element später mit dem gleichen Inhalt wieder lesen möchte.

Der Teil "that do not correspond to that member" ist in dem Satz nötig, 
weil er sonst ja sagen würde, dass auch die Bytes unspezifizierte Werte 
annehmen können, die zu dem gerade beschriebenen Element gehören. Nur 
weil ein Byte zu einem anderen Element gehört, heißt ja nicht, dass es 
nicht gleichzeitig auch zum gerade beschreibenen Element gehören kann. 
Das ist ja genau der Witz einer Union.

Also im Klartext: Die Bytes, die man beschreibt, wenn man auf ein 
Element zugreift, kann man später auch wieder richtig auslesen. Dass die 
Daten von anderen Elementen erhalten bleiben, auf die man vorher mal 
geschrieben hat, kann dabei aber nicht garantiert werden (da sich in der 
Praxis eben alle Elemente den gleichen Speicher teilen).

Mit der Länge der Elemente hat das nichts zu tun. Wenn alle Elemente 
gleich lang sind, ist es selbstverständlich, denn der Speicher ist ja 
nur einmal da. Bei unterschiedlich langen Elementen kann es aber halt 
auch sein, dass Teile von früher beschriebenen Elementen überleben. Das 
sichert einem der Standard aber nicht zu.

von Bronco (Gast)


Lesenswert?

Sehr interessant!
Ich hab bisher auch gedacht, daß der Sinn einer Union quasi ein 
erweiterter Cast sei, d.h. Daten mit einem Datentyp reinschreiben und 
mit anderem Datentyp wieder herauslesen.

von Oliver S. (oliverso)


Lesenswert?

Bronco schrieb:
> Ich hab bisher auch gedacht, daß der Sinn einer Union quasi ein
> erweiterter Cast sei, d.h. Daten mit einem Datentyp reinschreiben und
> mit anderem Datentyp wieder herauslesen.

Da hast du falsch gedacht. Allerdings wird die union seit ihrer 
Erfindung erfolgreich für solche "casts" genutzt, und in den 
allermeisten Fällen funktioniert das auch, egal, was der Standard dazu 
sagt.

Oliver

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Bronco schrieb:
> Sehr interessant!
> Ich hab bisher auch gedacht, daß der Sinn einer Union quasi ein
> erweiterter Cast sei, d.h. Daten mit einem Datentyp reinschreiben und
> mit anderem Datentyp wieder herauslesen.

Nein. Der Sinn und Zweck einer Union ist es, verschieden strukturierte 
Daten "übereinanderzulegen", um ein- und denselben Speicherplatz zu 
nutzen.

Ein Beispiel:

Ich habe ein Rack mit 24 Einschüben. In diese 24 Einschübe kann ich 
verschiedene Steuerkarten reinstecken. Jede dieser Steuerkarten (z.B. 
Echtzeituhr, Modul mit 16 Ausgängen, Modul mit 16 Eingängen) braucht 
verschiedene Betriebsparameter, z.B:

Echtzeituhr: Adresse, Stunde, Minute, Sekunde
16 Ausgänge: Adresse, 16 Bit Ausgänge
16 Eingänge: Adresse, 16 Bit Eingänge, Flags, wie z.B. Flankentriggerung

Dann könnte man definieren:

union
{
   struct rtc_st rtc;
   struct out_st outputs;
   struct in_st  inputs;
} slots[24];

Wenn ich jetzt eine Echtzeituhr in Slot 0 stecke, schreibe ich:

slots[0].rtc.address = 4711;
slots[0].rtc.hour = 14;
slots[0].rtc.minute = 22;
slots[0].rtc.second = 13;

Ich käme aber nie auf die Idee, die gesetzte Uhrzeit über ein anderes 
mögliches Elektronik-Modul (wie z.B. über slots[0].inputs) wieder 
auszulesen.

von Simon K. (simon) Benutzerseite


Lesenswert?

@Frank: Schöne Erklärung! Leider verhunzen viel zu viele Menschen die 
Union für Datenumwandlungen, anstatt es "richtig" (standardkonform) zu 
machen.

von Leo B. (luigi)


Lesenswert?

Wirklich schöne verständliche Erklärung, auch für nicht-profis wie mich.
Allerdings muss ich sagen, dass ich in den letzten Tagen gelernt habe, 
dass diese nicht-Standard-konforme Verwendung von unions sehr gut 
funktioniert und dem Compiler offenbar auch besser schmeckt als alles 
was ich sonst so probiert habe. Kleines Beispiel, zwei Wege ein globales 
volatile uint16_t ins eeprom zu schrieben:
1
        u16_t tmp;
2
        tmp.b.hi = spi_rx_buf[1];
3
 2fa:  70 91 68 00   lds  r23, 0x0068
4
        tmp.b.lo = spi_rx_buf[2];
5
 2fe:  60 91 69 00   lds  r22, 0x0069
6
        eeprom_write_word( &ee_temp_high, tmp.i );
7
 302:  82 e0         ldi  r24, 0x02  ; 2
8
 304:  90 e0         ldi  r25, 0x00  ; 0
9
 306:  14 d0         rcall  .+40       ; 0x330 <__eewr_word_tn24a>
10
11
12
        eeprom_write_word( &ee_temp_high, (spi_rx_buf[1]<<8) + spi_rx_buf[2] );
13
 308:  90 91 68 00   lds  r25, 0x0068
14
 30c:  80 91 69 00   lds  r24, 0x0069
15
 310:  d9 2f         mov  r29, r25
16
 312:  90 e0         ldi  r25, 0x00  ; 0
17
 314:  c9 2f         mov  r28, r25
18
 316:  be 01         movw  r22, r28
19
 318:  68 0f         add  r22, r24
20
 31a:  71 1d         adc  r23, r1
21
 31c:  82 e0         ldi  r24, 0x02  ; 2
22
 31e:  90 e0         ldi  r25, 0x00  ; 0
23
 320:  07 d0         rcall  .+14       ; 0x330 <__eewr_word_tn24a>
24
 322:  74 cf         rjmp  .-280      ; 0x20c <main+0x48>
wie man sieht, konnte der Compiler die Daten über den union-Missbrauch 
(u16_t definition im Anhang) mit wesentlich weniger Code ins eeprom 
verfrachten als z.B. mit der "(hi<<8)&lo"-Methode.

Ums wieder anfassbarer zu beschreiben. Wenn ich ein Loch entgraten 
möchte, muss ich da zwingend einen Entgrater verwenden, oder kann ich 
nicht auch einen großen Bohrer verwenden, wenn ich mir der Gefahr 
bewusst bin, mit dem Bohrer das Loch leicht zerstören zu können?

Anhang:
1
typedef struct
2
{
3
        uint8_t lo;
4
        uint8_t hi;
5
} hilobyte_t;
6
7
typedef union
8
{
9
        uint16_t i;
10
        hilobyte_t b;
11
} u16_t;

von Peter D. (peda)


Lesenswert?

Welchen Compiler benutzt Du denn?

Mit dem AVR-GCC 4.7.2 sieht es so aus:
1
void test()
2
{
3
  eeprom_write_word( &ee_temp_high, (spi_rx_buf[1]<<8) | spi_rx_buf[2] );
4
  54:   70 91 63 00     lds     r23, 0x0063
5
  58:   60 e0           ldi     r22, 0x00       ; 0
6
  5a:   80 91 64 00     lds     r24, 0x0064
7
  5e:   68 2b           or      r22, r24
8
  60:   80 e6           ldi     r24, 0x60       ; 96
9
  62:   90 e0           ldi     r25, 0x00       ; 0
10
  64:   03 c0           rjmp    .+6             ; 0x6c <__eewr_word_tn24>

Also gerade mal 2 Words mehr als mit Deiner Union.
Wieviel 1000-mal copy&pastest Du denn diese Codesequenz, damit das einen 
merkbaren Mehrverbrauch an Flash ergibt?


Peter

von Rolf Magnus (Gast)


Lesenswert?

Leo B. schrieb:
> wie man sieht, konnte der Compiler die Daten über den union-Missbrauch
> (u16_t definition im Anhang) mit wesentlich weniger Code ins eeprom
> verfrachten als z.B. mit der "(hi<<8)&lo"-Methode.

War da evtl. die Optimierung ausgeschaltet? Der Code kopiert ja munter 
völlig unnötig irgendwelche Register hin und her und macht allgemein 
jede Menge sinnloser Operationen.

von Leo B. (luigi)


Lesenswert?

Rolf Magnus schrieb:
> Leo B. schrieb:
>> wie man sieht, konnte der Compiler die Daten über den union-Missbrauch
>> (u16_t definition im Anhang) mit wesentlich weniger Code ins eeprom
>> verfrachten als z.B. mit der "(hi<<8)&lo"-Methode.
>
> War da evtl. die Optimierung ausgeschaltet? Der Code kopiert ja munter
> völlig unnötig irgendwelche Register hin und her und macht allgemein
> jede Menge sinnloser Operationen.

Der Meinung bin ich auch.
Ich nutze den AVR-GCC-4.7.2 mit -Os optimierung... liegt wohl wieder am 
volatile dass GCC da so nen mist baut

von Peter D. (peda)


Lesenswert?

Leo B. schrieb:
> liegt wohl wieder am
> volatile dass GCC da so nen mist baut

Nö, Du bist es, der ihm volatile befiehlt.

Mein Eindruck ist, daß Du volatile sehr freigiebig mit der Schöpfkelle 
verteilt hast.

Hör auf, nach einzelnen Words zu fischen, das bringt nichts.
Suche die Stellen, wo wirklich viel Code entsteht.


Peter

von Leo B. (luigi)


Lesenswert?

> Mein Eindruck ist, daß Du volatile sehr freigiebig mit der Schöpfkelle
> verteilt hast.
Auch wenn's nicht ganz Toppic ist:
volatile ist z.B. dort nötig, wo eine Interrupt-Routine Daten 
reinschreiben darf und die main-schleife sie raus lesen soll. liege ich 
da etwa falsch?

> Hör auf, nach einzelnen Words zu fischen, das bringt nichts.
> Suche die Stellen, wo wirklich viel Code entsteht.
Hab ich doch schon lange, drum bin ich ja bereits auf 884byte runter, 
aber in diesem Thread ist eben nicht das Thema "Codeoptimierung" sondern 
eben das effiziente Ansprechen der einzelnen Bytes eines uint16_t.
Ausserdem finde ich es gerade hoch interessant rauszufinden, wie man mit 
dem GCC-Compiler am effizientesten an die genannten Bytes ran kommt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Leo B. schrieb:

> Ich nutze den AVR-GCC-4.7.2 mit -Os optimierung... liegt wohl wieder am
> volatile dass GCC da so nen mist baut

Bist du sicher, daß die beiden Varianten, die du vergleichst, nicht 
Äpfel und Birnen sind?

Sind beide Varianten gleichbedeutend, insbesondere was deren 
Seiteneffekte (volatile) angeht?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Leo B. schrieb:

> Ausserdem finde ich es gerade hoch interessant rauszufinden, wie man mit
> dem GCC-Compiler am effizientesten an die genannten Bytes ran kommt.

Am effizientesten geht das, indem du es dem Compiler beibringst.  Die 
Quelle zu hacken ist keine Lösungen; es ist immer noch Hack...

Einen ersten Hinweis, wo der Code nicht so ist, wie du erwartest, 
findest du mit den Dumps aus

  -fdump-rtl-all -fdump-tree-all -dP -save-temps

Und nicht Äpfel mit Birnen vergleichen, gell?

von Leo B. (luigi)


Lesenswert?

Johann L. schrieb:
> Bist du sicher, daß die beiden Varianten, die du vergleichst, nicht
> Äpfel und Birnen sind?

ein uint16 aus einem volatile uint16_t array soll ins eeprom...
beide Methoden tun das, wo unterscheidet sich "dieser Apfel" von "dieser 
Birne"?

Johann L. schrieb:
> Einen ersten Hinweis, wo der Code nicht so ist, wie du erwartest,
> findest du mit den Dumps aus
>
>   -fdump-rtl-all -fdump-tree-all -dP -save-temps

Würde ich jetzt gerne verstehen, meine Kenntnisse sind aber offenbar zu 
gering. Was soll das bedeuten und was genau finde ich damit heraus?

Johann L. schrieb:
> Am effizientesten geht das, indem du es dem Compiler beibringst.

Darauf glaube ich habe ich keinen Einfluss solange ich mir keinen 
eigenen Compiler programmieren möchte, was ich definitiv nicht kann!

von Walter (Gast)


Lesenswert?

Leo B. schrieb:
> eeprom_write_word( &ee_temp_high, (spi_rx_buf[1]<<8) + spi_rx_buf[2] );


probier doch Mal statt dessen

uint16_t temp;
temp =  spi_rx_buf[1]<<8 + spi_rx_buf[2];
eeprom_write_word( &ee_temp_high, temp );

das sollte ähnlich kurz wie deine 1.Version sein

von Peter D. (peda)


Lesenswert?

Leo B. schrieb:
> volatile ist z.B. dort nötig, wo eine Interrupt-Routine Daten
> reinschreiben darf und die main-schleife sie raus lesen soll.

2 .. 3 Variablen als volatile sollte aber keinen merkbaren Einfluß auf 
die Gesamtgröße des Programms haben.

Du doktorst hier immer nur an kleinsten Codestückchen rum, die kaum 
Flash brauchen. Zeig dochmal die großen Brocken, da lohnt sich dann 
optimieren.


Peter

von Leo B. (luigi)


Angehängte Dateien:

Lesenswert?

Ich verspreche euch, da ist nichts gefälscht dran, aber...
mein avr-gcc hat folgende Version:
1
C:\Users\Leo\Documents\AVR\ATtiny24A\default>avr-gcc-4.7.2 --version
2
avr-gcc-4.7.2 (GCC) 4.7.2
3
Copyright (C) 2012 Free Software Foundation, Inc.
4
This is free software; see the source for copying conditions.  There is NO
5
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Wird aus dem AVR-Studio4  mit den Parametern im Anhang ausgeführt und 
compiliert aus dem anghängten Test.c das angehängte Test.lss
Warum? ich weiß es nicht. Compiliert bei irgend jemandem der avr-gcc 
etwa "besser"?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

-fno-split-wide-types wenn dich die paar Instruktionen stören. Ist 
vermutlich PR52278, evtl. verbandelt mit PR41076.

http://gcc.gnu.org/PR52278
http://gcc.gnu.org/PR41076

Keiner der von dir genannten Schalter ausser -Os ist ein 
Optimierungsschalter.

von Klaus (Gast)


Lesenswert?

Dein Code passt doch prima in den Tiny24, ist doch noch massig Platz 
übrig, vor allem, wenn du das zusammenbasteln deines ints nur einmal 
machst.

MfG Klaus

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Statt einem uint16 temp hätte ich eher erst mal die zwei uint8 in eine 
temp variable geschrieben also etwa so:
1
uint8_t hi = spi_rx_buf[1];
2
uint8_t lo = spi_rx_buf[2];
3
uint16_t word = ( hi << 8 ) + lo;
4
eeprom_write_word( &ee_temp_high, word );
Das entspricht eher dem, was du mit deiner Uinion machst.

von Klaus (Gast)


Lesenswert?

Läubi .. schrieb:
> uint16_t word = ( hi << 8 ) + lo;

Mal so eine Frage: warum wird hier (und auch weiter oben) eine Addition 
benutzt. Für mich ist das ein Zusammensetzen von Bitfeldern und dort 
wäre ein Oder gefragt? Bei Feldern kleiner als ein Byte macht man das 
doch auch nicht mit arithmetischen Operationen.

uint16_t word = ( hi << 8 ) | lo;

MfG Klaus

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Klaus schrieb:
> Mal so eine Frage: warum wird hier (und auch weiter oben)
> eine Addition benutzt.
Vom Ergbenis ist es (hier) gleichwertig, auch wenn ich hier eine | 
normalerweise bevorzuge, ich habe aber den "orginal" Code einfach 
kopiert und nicht darauf geachtet denk dir einfach ein | anstelle des + 
:-)

Eventuell wird deshalb auch soviel unützer Code rangezogen, kann der TE 
ja mal für sich ausprobieren.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Klaus schrieb:
> Mal so eine Frage: warum wird hier (und auch weiter oben) eine Addition
> benutzt.

Erstmal ist die Addition hier nicht grundsätzlich falsch. Trotzdem hast 
Du schon recht: eigentlich ist es Unsinn.

Der Grund, warum es doch einige Leute so machen:

Es gab in der Vergangenheit gcc-Versionen, welche bei der Plus-Variante 
kürzeren Code als bei der Oder-Variante erzeugt haben. So steckt das '+' 
hier manchen halt "im Blut". Bei aktuellen gcc-Versionen spielt das 
keine Rolle mehr. Der erzeugte Code ist dann exakt gleich lang, sieht 
aber nicht gleich aus.

von Peter D. (peda)


Lesenswert?

Es ist schon erstaunlich, welche Unmenge an Zeit investiert wird, um nur 
wenige Words einzusparen. Mir wäre meine Zeit dafür zu schade.

Effizienter ist es, auf der Programmablaufseite zu optimieren. Dann wird 
meistens der Code auch einfacher und leserlicher.
Auch braucht man keine speziellen Compilertricks.


Peter

von Fabian O. (xfr)


Lesenswert?

Naja, ich finde es schon bemerkenswert, dass der Compiler so etwas 
simples nicht optimal hinbekommt. Ist ja nicht so, dass das 
Zusammensetzen von 16 Bit aus zwei Mal 8 Bit und Übergabe an eine 
Funktion so selten vorkäme. Da habe ich den GCC wirklich überschätzt.

Habe gestern auch ein bisschen mit dem Beispiel rumgespielt. Diese 
Variante war auch dabei, aber da werden die Daten ebenfalls unnötig in 
Registern rumkopiert:
1
uint8_t hi = spi_rx_buf[1];
2
uint8_t lo = spi_rx_buf[2];
3
uint16_t word = ( hi << 8 ) | lo;
4
eeprom_write_word( &ee_temp_high, word );

Was solls, sehen wir es positiv: In zukünftigen Versionen steckt noch 
Potenzial für kleinere Codegrößen ...

von Peter D. (peda)


Lesenswert?

Fabian O. schrieb:
> In zukünftigen Versionen steckt noch
> Potenzial für kleinere Codegrößen ...

Sehe ich nicht so.
Volatile ist ja nichts, was den Großteil eines Programms ausmacht, also 
wird man daran (vermutlich) auch nichts optimieren.
Im Gegenteil, man wird darauf bedacht sein, daß bei volatile nicht 
zuviel wegoptimiert wird.

Die Compilerbauer sind extrem daran interessiert, daß ihre Arbeit auch 
einen großen Effekt hat, ehe sie etwas ändern.

Wenn ich mal überlege wieviel Jahre es gedauert hat, den 64Bit-Typen 
etwas Sparsamkeit zu gönnen und da ging es um satte ~5kB!

Statt bei volatile um einzelne Words zu kämpfen, finde eine double Lib 
erheblich sinnvoller. Mit float stößt man schon sehr oft an 
Genauigkeitsgrenzen.


Peter

von Oliver S. (oliverso)


Lesenswert?

Fabian O. schrieb:
> Ist ja nicht so, dass das
> Zusammensetzen von 16 Bit aus zwei Mal 8 Bit und Übergabe an eine
> Funktion so selten vorkäme.

Da sei die Frage erlaubt, was du unter "nicht selten" verstehst.

Peter hats ja schon geschrieben: Wer sich über die dabei 
"verschwendeten" Bytes Gedanken macht, optimiert auf einer Ebene, die 
bei 99% der Anwendungen und der Anwender schlicht uninteressant ist. 
Beim restlichen Prozent ist entweder gcc der falsche Compiler, oder der 
Programmierer für das Projekt nicht geeignet.

Oliver

von Peter D. (peda)


Lesenswert?

Ein Frage ist natürlich, warum speicherst Du nicht einfach direkt die 2 
Byte im EEPROM?
1
eeprom_update_block ( &spi_rx_buf[1], &ee_temp_high, 2 );


Peter

von Fabian O. (xfr)


Lesenswert?

Peter Dannegger schrieb:
> Volatile ist ja nichts, was den Großteil eines Programms ausmacht, also
> wird man daran (vermutlich) auch nichts optimieren.
> Im Gegenteil, man wird darauf bedacht sein, daß bei volatile nicht
> zuviel wegoptimiert wird.

Der Fehler hat ja nichts mit volatile zu tun. In dem Beispiel waren die 
beiden Einzelbytes in getrennten nicht-volatile-Variablen. Der Übergabe 
dieser beiden Variablen als ein 16-Bit-Wert an die Funktion ist das 
Problem. Denn anstatt sie gleich in die richtigen Register zu legen 
kommen sie zuerst in ein temporäres Register, werden mit Null verodert 
und dann erst an die richtige Stelle kopiert.

Johann hat sogar die entsprechenden Bugs genannt:
http://gcc.gnu.org/PR52278
http://gcc.gnu.org/PR41076

Oliver S. schrieb:
> Da sei die Frage erlaubt, was du unter "nicht selten" verstehst.

Praktisch überall, wo man 16 Bit von außen (einem Sensor, UART, 
Netzwerk, Funk, ...) bekommt und weiterverarbeitet. Die Werte kommen 
immer zuerst byteweise über ein Interface rein und müssen im Programm zu 
einem uint16_t zusammengebaut werden. Wenn da jedes Mal solche 
Sperenzchen gemacht werden, ist das in Summe imo schon bedeutsam für 
Codegröße und Geschwindigkeit. Ich glaube/hoffe allerdings, dass dieser 
Fehler nur unter bestimmten, seltenen Umständen auftritt, sonst wäre das 
ja sicher schon mal in den Testfällen aufgefallen.

von Peter D. (peda)


Lesenswert?

Fabian O. schrieb:
> Die Werte kommen
> immer zuerst byteweise über ein Interface rein und müssen im Programm zu
> einem uint16_t zusammengebaut werden.

Das sind dann je Interface an genau 2 Stellen (Lesen, Schreiben), also 
nicht der Rede wert.

Fabian O. schrieb:
> Wenn da jedes Mal solche
> Sperenzchen gemacht werden

Nur wenn man es als Spaghetticode (lange copy&paste Monster) an 
hunderten Stellen macht.
Damit rechnen die Compilerbauer aber nicht, daß jemand so programmiert.
Derjenige muß dann eben nen größeren MC nehmen oder seinen 
Programmierstil verbessern.


Peter

von Fabian O. (xfr)


Lesenswert?

Es geht nicht nur um Codegröße, sondern auch um 
Ausführungsgeschwindigkeit. Selbst wenn die unnötigen Befehle nur an 
einer einzigen Stelle im Programm stehen, können sie mehrere tausend Mal 
pro Sekunde ausgeführt werden (Beispiel: externer ADC).

Da hilft dann kein größerer MC oder "Programmierstil verbessern", 
sondern das Gegenteil, nämlich den Hack über die Union zu gehen oder den 
Teil in (Inline-)Assembler schreiben. Beides kein guter Programmierstil, 
wenn es der Compiler genauso gut könnte.

Ich kann nicht nachvollziehen, wie man so einen offensichtlichen Bug 
verteidigen kann. Klar geht die Welt davon nicht unter, aber es ist 
nunmal ein Fehler des Compilers und nicht des Programmierers.

von Oliver S. (oliverso)


Lesenswert?

Na ja, für den einen ist das ein unverzeihlicher Bug, für andere ist das 
zwar nicht perfekt, aber man kommt damit klar. Kein Compiler dieser Welt 
erzeugt für alle denkbaren Sourcecodes den absolut 
optimalen/schnellsten/kürzesten/ was auch immer Code. Wer das erwartet, 
dem fehlt etwas Realitätssinn.

(avr-)gcc erzeugt unbetritten bei den 8-bittern nicht immer optimalen 
Code.
Aber niemand muß einen bestimmten Compiler nutzen, und jeder darf beim 
gcc Hand anlegen, und mithelfen, den zu verbessern. Der erste Schritt 
wäre ein Bug-Report, auch wenn das zugegebermassen nichts bringen wird, 
da sich die wenigen Resourcen dort lieber um die wirklich wichtigen 
Probleme kümmern.

Oliver

von Peter D. (peda)


Lesenswert?

Fabian O. schrieb:
> Selbst wenn die unnötigen Befehle nur an
> einer einzigen Stelle im Programm stehen, können sie mehrere tausend Mal
> pro Sekunde ausgeführt werden

1000 mal/s sind 16000 Zyklen bei 16MHz, da willst Du wirklich um jeden 
einzelnen Zyklus feilschen?
Mir wär das zu blöde.

Beim Ursprungspost ist das ja besonders kritisch. 6,8ms Schreiben in den 
EEPROM, da sind zusätzliche 62,5ns bestimmt absolut tödlich :-)


Fabian O. schrieb:
> nämlich den Hack über die Union zu gehen oder den
> Teil in (Inline-)Assembler

Wenn Du damit glücklicher wirst.
Ich komme bestens ohne solche Krücken aus. Und manche Projekte laufen 
sogar nur mit 1MHz.


Einen Overhead von 20% bei einem C-Programm gegenüber Assembler finde 
ich völlig normal. Erst ab 100% würde ich mir (vielleicht) Gedanken 
machen.


Peter

von Peter D. (peda)


Lesenswert?

Ich hab mir auch mal Krücken für nen CAN-Bus machen müssen:
1
uint32_t swap_order( uint32_t val )             // LSB first -> MSB first
2
{
3
  union{
4
    uint32_t u32;
5
    uint8_t u8[4];
6
  } in, out;
7
  in.u32 = val;
8
  out.u8[0] = in.u8[3]; 
9
  out.u8[1] = in.u8[2]; 
10
  out.u8[2] = in.u8[1]; 
11
  out.u8[3] = in.u8[0]; 
12
  return out.u32;
13
}
Das wurde von AVR-GCC recht effizient umgesetzt. Besser hätte ich es in 
Assembler auch nicht machen können.

Und dann noch, um ein float als int32 zu übertragen ohne Cast:
1
static inline uint32_t mkp_u32( float val )    // make pointer from float to uint32_t
2
{
3
  union{
4
    uint32_t u32;
5
    float f;
6
  } out;
7
  out.f = val;
8
  return out.u32;
9
}
10
11
static inline float mkp_fl( uint32_t val )      // make pointer from uint32_t to float
12
{
13
  union{
14
    uint32_t u32;
15
    float f;
16
  } out;
17
  out.u32 = val;
18
  return out.f;
19
}
Diese Funktionen erzeugen 0 Byte Code, sie wandeln nur den Typ um.

Ganz ohne Union geht es manchmal doch nicht.


Peter

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Fabian O. schrieb:
> Ich kann nicht nachvollziehen, wie man so einen offensichtlichen Bug
> verteidigen kann. Klar geht die Welt davon nicht unter, aber es ist
> nunmal ein Fehler des Compilers und nicht des Programmierers.

Niemand bestreitet, daß der Code suboptimal ist, aber es ist definitiv 
kein Fehler.

Fabian O. schrieb:
> Der Fehler hat ja nichts mit volatile zu tun.

Korrekt.  Wobei es aber auch Situationen gibt, wo für volatile besserer 
Code erzeugt werden könnte, etwa PR49807.

> Johann hat sogar die entsprechenden Bugs genannt:
> http://gcc.gnu.org/PR52278
> http://gcc.gnu.org/PR41076

Das "PR" steht weder für "Bug" noch für "Fehler", sondern für "Problem 
Report".

> Ich glaube/hoffe allerdings, dass dieser Fehler nur unter bestimmten,
> seltenen Umständen auftritt, sonst wäre das ja sicher schon mal in
> den Testfällen aufgefallen.

Gesetzt den Fall, es gäbe solch einen Testfall in der GCC-Testuite [2]. 
Wem bitte sollte das Problem aussallen?  Es werden ja noch nichtmal 
regelmässig AVR-Tests gefahren, siehe [1].  Nach "avr" suchst du da 
vergebens, zumindest die letzten 10 Monate.

Ausserdem ist das Problem bekannt wie du an den genannten PRs siehst.

Es ist allerdings so, daß es niemanden gibt, der sich um 
avr-gcc kümmert.  Weil es niemanden interessiert oder niemand Bock drauf 
hat oder niemand weiß oder wissen will wie es geht oder weil niemand es 
als wichtig genug erachtet, professionellen Support dafür zu 
organisieren oder zu bezahlen — noch nicht mal Atmel.

Und anderen GCC-Entwicklern von ARM oder von IBM oder von Google oder 
von wo auch immer kann man schwerlich einen Vorwurf daraus machen, sich 
nicht um eine ferner-liefern Architektur zu kümmern, von der sie noch 
nie was gehört haben oder wissen wofür "AVR" steht.

Kurzum: Jeder, der sich auch nur ein bisschen mit AVRs auskennt, weiß, 
wie guter Code aussieht.  Jeder, der sich nur ein bisschen mit avr-gcc 
beschäftigt hat — und sei es nur als Anwender — weiß, daß dieser 
Compiler keinen optimalen Code erzeugt und daß se keinen Support gibt, 
weil es niemanden auf dem Planeten gibt, dem es wichtig genug ist.


[1] http://gcc.gnu.org/ml/gcc-testresults
[2] http://gcc.gnu.org/viewcvs/trunk/gcc/testsuite/

von Rolf Magnus (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Und dann noch, um ein float als int32 zu übertragen ohne Cast:

Und warum muß es unbedingt ohne Cast sein? Ich hab's grad mal mit dem 
avr-gcc 4.7.0 ausprobiert, und da kommt bei mir für die union, den Cast 
und das memcpy immer das selbe raus:
1
#include <stdint.h>
2
#include <string.h>
3
4
static inline uint32_t mkp_u32( float val )    // make pointer from float to uint32_t
5
{
6
  union{
7
    uint32_t u32;
8
    float f;
9
  } out;
10
  out.f = val;
11
  return out.u32;
12
}
13
14
void foo(uint32_t f);
15
16
void test1(float val)
17
{
18
    foo(mkp_u32(val));
19
}
20
21
void test2(float val)
22
{
23
    foo(*(uint32_t*)&val);
24
}
25
26
void test3(float val)
27
{
28
    uint32_t tmp;
29
    memcpy(&tmp, &val, sizeof(val));
30
    foo(tmp);
31
}

Und hier der generierte Assembler-Code:
1
        .file   "convert.c"
2
__SP_H__ = 0x3e
3
__SP_L__ = 0x3d
4
__SREG__ = 0x3f
5
__tmp_reg__ = 0
6
__zero_reg__ = 1
7
        .text
8
.global test1
9
        .type   test1, @function
10
test1:
11
/* prologue: function */
12
/* frame size = 0 */
13
/* stack size = 0 */
14
.L__stack_usage = 0
15
        rjmp foo
16
        .size   test1, .-test1
17
.global test2
18
        .type   test2, @function
19
test2:
20
/* prologue: function */
21
/* frame size = 0 */
22
/* stack size = 0 */
23
.L__stack_usage = 0
24
        rjmp foo
25
        .size   test2, .-test2
26
.global test3
27
        .type   test3, @function
28
test3:
29
/* prologue: function */
30
/* frame size = 0 */
31
/* stack size = 0 */
32
.L__stack_usage = 0
33
        rjmp foo
34
        .size   test3, .-test3
35
        .ident  "GCC: (GNU) 4.7.0"

Wie man sieht, wird in allen drei Fällen kein Code für die Konvertierung 
erzeugt.

PS: Ich bin erstaunt, daß sogar das rcall/ret zu einem rjump optimiert 
wird. Und da beschweren sich hier alle über die schlechte Optimierung.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Rolf Magnus schrieb:

> Und warum muß es unbedingt ohne Cast sein? Ich hab's grad mal mit dem
> avr-gcc 4.7.0 ausprobiert, und da kommt bei mir für die union, den Cast
> und das memcpy immer das selbe raus:

> void test2(float val)
> {
>     foo(*(uint32_t*)&val);
> }

Das ist kein Cast sondern Type-Punning ;-)  Wenn mach durch einen 
int-Zeiger in den Speicher greift (oder long, ist egal), kann der 
Compiler davon ausgehen, daß ein float-Schrieb diese Sppeicherstelle 
nicht verändert. Nennt sich Strict-Aliasing.

GCC sollte eine Warnung wie "type punning breaks strict aliasing rules" 
o.ä. bringen.

von Peter D. (peda)


Lesenswert?

@Rolf Magnus

Ich wollte mir damit die extra Zwischenvariablen ersparen, z.B.:
1
void tx_can_msg( uint32_t data);
2
float get_val();
3
4
static inline uint32_t mkp_u32( float val )    // make pointer from float to uint32_t
5
{
6
  union{
7
    uint32_t u32;
8
    float f;
9
  } out;
10
  out.f = val;
11
  return out.u32;
12
}
13
14
void test()
15
{
16
  tx_can_msg( mkp_u32( get_val()));
17
}
D.h. weder Quelle noch Ziel haben eine echte Adresse.

Ich hantiere nur ungern mit globalen Variablen, sondern nutze für alles 
Funktionen.


Peter

von Rolf Magnus (Gast)


Lesenswert?

Peter Dannegger schrieb:
> @Rolf Magnus
>
> Ich wollte mir damit die extra Zwischenvariablen ersparen,

Ok. Um die ging es aber ja an sich gar nicht, sondern um die union, die 
hier keinen Vorteil gegenüber anderen Methoden der Typ-Reinterpretation 
hat. Die memcpy-Variante produziert auch nicht mehr Code, ist aber immer 
noch weniger zu schreiben als die union, und ich finde sie auch besser 
lesbar. Mir ist einfach nicht klar, warum so oft darauf bestanden wird, 
trotzdem auf Teufel komm raus die eigentlich dafür gar nicht gedachte 
union  zu mißbrauchen.

Johann L. schrieb:
> Das ist kein Cast sondern Type-Punning ;-)

Naja, ein Cast kommt auch drin vor ;-)

> Wenn mach durch einen int-Zeiger in den Speicher greift (oder long, ist
> egal), kann der Compiler davon ausgehen, daß ein float-Schrieb diese
> Sppeicherstelle nicht verändert. Nennt sich Strict-Aliasing.

Ich hab ehrlich gesagt nie verstanden, was es damit auf sich hat.

> GCC sollte eine Warnung wie "type punning breaks strict aliasing rules"

Ja, diese Warnung kommt.
1
convert.c:23:5: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Rolf Magnus schrieb:

> Ich hab ehrlich gesagt nie verstanden, was es damit [Strict-Aliasing]
> auf sich hat.

Beispiel:
 
1
extern long x;
2
3
long f (float *y)
4
{
5
    x = 0;
6
    *y = 42;
7
    return x;
8
}
 
darf ein C-Compiler übersetzen wie
 
1
extern long x;
2
3
long f (float *y)
4
{
5
    x = 0;
6
    *y = 42;
7
    return 0;
8
}
 
und avr-gcc tut das auch:
 
1
f:
2
  sts x,__zero_reg__   ;  x,
3
  sts x+1,__zero_reg__   ;  x,
4
  sts x+2,__zero_reg__   ;  x,
5
  sts x+3,__zero_reg__   ;  x,
6
  ldi r20,0   ;  tmp44
7
  ldi r21,0   ; 
8
  ldi r22,lo8(40)   ; ,
9
  ldi r23,lo8(66)   ; ,
10
  movw r30,r24   ; , y
11
  st Z,r20   ;  *y_1(D), tmp44
12
  std Z+1,r21   ;  *y_1(D), tmp44
13
  std Z+2,r22   ;  *y_1(D), tmp44
14
  std Z+3,r23   ;  *y_1(D), tmp44
15
  ldi r22,0   ; 
16
  ldi r23,0   ; 
17
  movw r24,r22   ; 
18
  ret
 

Konkret: Verändern eines Objektes A (hier ein float) wird kein dazu 
"inkompatibles" Objekt (hier long) ändern.  Ausnahme ist char.  Nun 
überleg dir, was das für deinen Type-Punning Code bedeutet :-)

Peter Dannegger schrieb:
> Ich hab mir auch mal Krücken für nen CAN-Bus machen müssen:
> uint32_t swap_order( uint32_t val )
> first
> {
>   union{
>     uint32_t u32;
>     uint8_t u8[4];
>   } in, out;
>   in.u32 = val;
>   out.u8[0] = in.u8[3];
>   out.u8[1] = in.u8[2];
>   out.u8[2] = in.u8[1];
>   out.u8[3] = in.u8[0];
>   return out.u32;
> }

Schon mal einen Gedanken an __builtin_bswap32 verschwendet?

http://gcc.gnu.org/viewcvs/trunk/libgcc/config/avr/lib1funcs.S?revision=193721&view=markup#l2723

von Rolf Magnus (Gast)


Lesenswert?

Johann L. schrieb:
> Konkret: Verändern eines Objektes A (hier ein float) wird kein dazu
> "inkompatibles" Objekt (hier long) ändern.  Ausnahme ist char.

Ok, verstanden.

> Nun überleg dir, was das für deinen Type-Punning Code bedeutet :-)

Hmm, ich hätte jetzt auf "nichts" getippt. In meinem Fall wird während 
der kurzen Existenz des Zeigers ja nichts verändert.

Rolf Magnus schrieb:
> void test2(float val)
> {
>     foo(*(uint32_t*)&val);
> }

Oder ist es einfach so, daß die Warnung vorgeschrieben ist?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Das sieht aber ganu anders aus, wenn die Funktion beim Aufruf bekannt 
ist, etwa weil sie im gleichen Modul steh und / oder geinlint wird, per 
Makro keingeklöppelt wird oder mit Optimierungen wie LTO übersetzt wird, 
die modulübergreifend optimieren.

von Fabian O. (xfr)


Lesenswert?

Johann L. schrieb:
> Niemand bestreitet, daß der Code suboptimal ist, aber es ist definitiv
> kein Fehler.
>
> Das "PR" steht weder für "Bug" noch für "Fehler", sondern für "Problem
> Report".

Gut, im Sinne des C-Standards ist es natürlich kein Fehler, denn der 
übersetzte Code macht was er soll. Dafür bin ich ja auch sehr dankbar.

>> Ich glaube/hoffe allerdings, dass dieser Fehler nur unter bestimmten,
>> seltenen Umständen auftritt, sonst wäre das ja sicher schon mal in
>> den Testfällen aufgefallen.
>
> Gesetzt den Fall, es gäbe solch einen Testfall in der GCC-Testuite [2].
> Wem bitte sollte das Problem aussallen?  Es werden ja noch nichtmal
> regelmässig AVR-Tests gefahren, siehe [1].  Nach "avr" suchst du da
> vergebens, zumindest die letzten 10 Monate.

Das ist schade. Ich habe von der Entwicklung des GCC leider keine 
Ahnung, daher bitte nicht böse sein, wenn ich utopische Vorstellungen 
habe. Ich hätte mir vorgestellt, dass es für jede Optimierung eine Reihe 
von Testfällen gibt, die nur erfolgreich sind, wenn der Compiler die 
Optimierung auf den Testfall korrekt anwendet. Einer davon hätte eben 
sein können "Einen 16/32/64-Bit-Wert aus einzelnen Bytes per 
Shiftoperator zusammensetzen. Der übersetzte Code darf maximal x Befehle 
lang sein". Ich sehe aber schon, dass das wegen der unterschiedlichen 
Architekturen, die der GCC unterstützt, schwierig ist.

> Ausserdem ist das Problem bekannt wie du an den genannten PRs siehst.
>
> Es ist allerdings so, daß es niemanden gibt, der sich um
> avr-gcc kümmert.  Weil es niemanden interessiert oder niemand Bock drauf
> hat oder niemand weiß oder wissen will wie es geht oder weil niemand es
> als wichtig genug erachtet, professionellen Support dafür zu
> organisieren oder zu bezahlen — noch nicht mal Atmel.

Das ist erst recht schade und war mir so nicht bewusst. Dann ist auch 
verständlich, dass solche "Probleme" keine hohe Priorität haben.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Fabian O. schrieb:
> Johann L. schrieb:
>> Gesetzt den Fall, es gäbe solch einen Testfall in der GCC-Testuite.
>> Wem bitte sollte das Problem auffallen?  Es werden ja noch nichtmal
>> regelmässig AVR-Tests gefahren.  Nach "avr" suchst du da
>> vergebens, zumindest die letzten 10 Monate.
>
> [...] Ich hätte mir vorgestellt, dass es für jede Optimierung eine Reihe
> von Testfällen gibt, die nur erfolgreich sind, wenn der Compiler die
> Optimierung auf den Testfall korrekt anwendet. Einer davon hätte eben
> sein können "Einen 16/32/64-Bit-Wert aus einzelnen Bytes per
> Shiftoperator zusammensetzen. Der übersetzte Code darf maximal x Befehle
> lang sein". Ich sehe aber schon, dass das wegen der unterschiedlichen
> Architekturen, die der GCC unterstützt, schwierig ist.

Es gibt ja auch architekturabhängige Tests für das avr-Target:

http://gcc.gnu.org/viewcvs/trunk/gcc/testsuite/gcc.target/avr

Du kannst also einen Test schreiben und schauen, ob er durchgeht bzw. 
die erwarteten Probleme korrekt rausfiltert.  Wie GCC intern 
funktioniert, brauchst du dafür nicht zu wissen.

>> Ausserdem ist das Problem bekannt wie du an den genannten PRs siehst.
>>
>> Es ist allerdings so, daß es niemanden gibt, der sich um
>> avr-gcc kümmert.  Weil es niemanden interessiert oder niemand Bock drauf
>> hat oder niemand weiß oder wissen will wie es geht oder weil niemand es
>> als wichtig genug erachtet, professionellen Support dafür zu
>> organisieren oder zu bezahlen — noch nicht mal Atmel.
>
> Das ist erst recht schade und war mir so nicht bewusst. Dann ist auch
> verständlich, dass solche "Probleme" keine hohe Priorität haben.

Die Priorität ist ziemlich wurscht.  Nimm einfach diese Forum als 
Beispiel und was passiert, wenn jemand eine gaaanz dringendes Problem 
hat und eine (An)frage stellt wie:

   "Achtung Wichtig! Habe Problem mit SLE 78!
    Bitte schnell beantworten! DRINGEND!!!"

Glaubst du, nur weil jemand da wichtig-wichtig hinschreibt, wird hier 
jeder alles stehen und liegen lassen, sich in den SLE 78 einarbeiten, 
nur um sein Problem zu beheben?

Noch unrealistischer wird es, wenn Wichtig-Wichtig nicht nur eine Frage 
hat, sondern Code haben will oder einen Schaltplan braucht und dafür 
Entwicklung unternommen werden muss.  Glaubst du, daß Wichtig-Wichtig 
eine schnelle — ober überhaupt eine — Antwort bekommen wird, nur weil es 
für ihn wichtig ist?

Auf AVR+GCC bezogen: Für eine Architektur gibt es die 1. Klasse, die 2. 
Klasse und die 3. Klasse.  3x darfst du raten, on AVR drittklassigen 
Support genießt oder nicht...  U.a. ist AVR drittklassig weil:

1) Es keinen C++ Support gibt (libsupc++)

2) Es nicht genügend (konkret: überhaupt keine) Entwickler gibt,
   die auftretende Probleme zeitnah beheben, so daß zB eine GCC-Release
   nicht wegen AVR bis zu St. Nimmerlein verschoben werden muss.

3) avr-gcc ist nicht Standard-konform, z.B. double

Am C++ Support ist offenbar niemand interessiert — wobei ich unter 
Interesse verstehe, daß sich diesbezüglich was tut in GCC.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Johann L. schrieb:
> Es nicht genügend (konkret: überhaupt keine) Entwickler gibt

Ich kann deine Frustration diesbezüglich gut verstehen, man muss aber 
auch sehen, dass leider die Einstiegshürde bei GCC sehr hoch ist was 
einfach viele abschreckt. Das eine Firma wie Atmel lieber ihr eigenes 
Süppchen kocht anstatt aktiv mitzuwirken ist dann natürlich doppelt 
Schade.

Johann L. schrieb:
> Es keinen C++ Support gibt (libsupc++)

Was "fehlt" den da? Ich dachte hier schon öfter C++ für AVR gesehen zu 
haben.

von Bronco (Gast)


Lesenswert?

Johann L. schrieb:
> Beispiel:
1
 
2
 extern long x;
3
 long f (float *y)
4
 {
5
     x = 0;
6
     *y = 42;
7
     return x; 
8
 }
>darf ein C-Compiler übersetzen wie
1
 
2
 extern long x; 
3
 long f (float *y)
4
 {
5
     x = 0;
6
     *y = 42;
7
     return 0;
8
 }

Da ich in diesem Thread auch zum ersten mal mit "Aliasing" konfrontiert 
wurde:
Darf der Compiler das grundsätzlich immer (und seit je her, also auch 
vor C99), oder darf er das nur, wenn "strict aliasing" explizit 
eingeschaltet ist?
Oder anders herum: Kann ich mich darauf verlassen, daß er das nicht so 
optimiert, wenn ich "strict aliasing" nicht explizit eingeschaltet habe?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Läubi .. schrieb:
> Johann L. schrieb:
>> Es nicht genügend (konkret: überhaupt keine) Entwickler gibt
>
> Ich kann deine Frustration diesbezüglich gut verstehen, man muss aber

Keine Frustration, eher erstaunen darüber, welche Erwartungshaltung es 
an avr-gcc gibt und welche Vorstellung darüber, wer sich wie intensiv 
darum kümmert.

> auch sehen, dass leider die Einstiegshürde bei GCC sehr hoch ist was
> einfach viele abschreckt.

Ja, stimmt wohl.  Die Lernkurve des GCC ist irgendwo zwischen vertikal 
und senkrecht unzusiedeln.  Wenn GCC es nicht schafft, für (potentielle) 
Entwickler so einfach handhabbar zu sein und einen so leichen Einstieg 
zu bieten wie etwa LLVM, dann wird er m.E. über kurz oder lang nicht 
damit  LLVM mithalten können.

Nichtsdestotrotz wird avr-gcc breiter eingesetzt als LLVM, zumindest ist 
das mein Eindrück, den ich auch nicht weiter belegen kann.

> Das eine Firma wie Atmel lieber ihr eigenes Süppchen kocht anstatt
> aktiv mitzuwirken ist dann natürlich doppelt Schade.

Das ist ja Firmenpolitik von Atmel und deren Entscheidung wie sie mit 
GCC umgehen.  Immerhin war ihnen der AVR32 einen eigenen Port wert...

Die englische Wikipedia meint zum GCC:

  "Several companies make a business out of supplying and
   supporting GCC ports to various platforms, and chip manufacturers
   today consider a GCC port almost essential to the success of an
   architecture."


> Johann L. schrieb:
>> Es keinen C++ Support gibt (libsupc++)
>
> Was "fehlt" den da? Ich dachte hier schon öfter C++ für AVR gesehen zu
> haben.



Bronco schrieb:
> Darf der Compiler das grundsätzlich immer (und seit je her, also auch
> vor C99), oder darf er das nur, wenn "strict aliasing" explizit
> eingeschaltet ist?
> Oder anders herum: Kann ich mich darauf verlassen, daß er das nicht so
> optimiert, wenn ich "strict aliasing" nicht explizit eingeschaltet habe?

Eine C-Compiler erzeugt Code, der auf der realen Maschine (zB AVR) die 
gleichen Seiteneffekte hat wie für die abstrakte Maschine spezifiziert. 
Und dazu gehört eben auch, daß Änderung eines float keinen long ändern 
kann etc.

In GCC können entsprechende Optimierungen mit -fno-strict-aliasing 
deaktiviert werden.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Läubi .. schrieb:
> Johann L. schrieb:
>> Es keinen C++ Support gibt (libsupc++)
>
> Was "fehlt" den da? Ich dachte hier schon öfter C++ für AVR gesehen zu
> haben.

avr-g++ ist i.W der nackte Compiler mit libgcc- und libc-Unterstützung 
(wobei letztere kein Teil von GCC ist).

Konkret heißt das, daß nocht nichtmal der Sprachkern voll unterstützt 
wird.  Übersetz einfach mal
 
1
int v;
2
3
int main (void)
4
{
5
  try
6
  {
7
    throw 42;
8
  }
9
  catch (int e)
10
  {
11
    v = e;
12
  }
13
}
  
Vor der Codedichte mal ganz abgesehen...

von Peter D. (peda)


Lesenswert?

Wäre mir neu, daß der AVR Exceptions erzeugen kann. Er hat dafür weder 
Vectoren noch Flags.
Z.B. ein Zugriff auf ungültigen Flash oder RAM oder ungültigen Opcode 
führt er klaglos aus.


Peter

von Rolf Magnus (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Wäre mir neu, daß der AVR Exceptions erzeugen kann. Er hat dafür weder
> Vectoren noch Flags.

Hä?

> Z.B. ein Zugriff auf ungültigen Flash oder RAM oder ungültigen Opcode
> führt er klaglos aus.

Ach solche Exceptions meinst du. Die haben rein gar nichts mit 
C++-Exceptions zu tun.

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.