Forum: Compiler & IDEs GCC Toolchain - Interpretation einer Warnung


von Frickelfritze (Gast)


Lesenswert?

Der GCC meldet unter Atmel Studio 7.0 bei folgendem Code eine Warnung.
1
uint16_t  CalculateChecksum (uint8_t *pFrom, uint16_t BlockSize)
2
{
3
    uint16_t  nChecksum = 0;
4
    uint16_t  nIndex;
5
    uint16_t  val16;
6
    uint16_t  *ptr16;
7
8
    ptr16 = (uint16_t*)pFrom;
9
    for (nIndex = 0; nIndex<(BlockSize/2); nIndex++)
10
    {
11
        val16 = *(ptr16 + nIndex);
12
        nChecksum = nChecksum + val16;
13
    }
14
    return nChecksum;
15
}

genau genommen ist es die Zeile
1
    ptr16 = (uint16_t*)pFrom;
Der Cast bildet also einen uint16_t* Datentyp aus einem 8-Bit-
Pointer.

Die Warnung lautet:

cast increases required alignment of target type

Kann das jemand genauer erklären? Braucht der Compiler ein Alignment
auf 32 Bit um pointern zu können? Ist das dann "Verschwendung" von
Speicherplatz? Fragen über Fragen ....

von Frickelfritze (Gast)


Lesenswert?

Frickelfritze schrieb:
> Der GCC meldet unter Atmel Studio 7.0 bei folgendem Code eine Warnung.

Ach so ja, habe ich vergessen: Es handelt sich um ein SAM3X Kompilat.
Also ARM 32 bit .....

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


Lesenswert?

Frickelfritze schrieb:
> Braucht der Compiler ein Alignment
> auf 32 Bit um pointern zu können?

Nein, aber du hast einen Zeiger, der auf 8-Bit-Daten zeigt, der kann
also jedes Byte adressieren.  Jetzt machst du einen draus, der
16-Bit-Daten adressieren können soll.  Diese können aber nur auf
jeder geraden Adresse stehen.  Wenn dein Zeiger vorher ganz legal auf
eine ungerade Adresse gezeigt hat, zeigt er jetzt auf etwas, auf dem
legal keine 16-Bit-Zahl stehen kann … Die Folge kann eine Pessimierung
der Zugriffe sein (Prozessor macht zwei Speicherzugriffe und baut sich
seine 16-Bit-Zahl daraus) oder auch ein misalignment trap / fault.

Schlechtes Design so.

Entweder popelst du dir deine 16-Bit-Zahl gleich aus den 8-Bit-Werten
zusammen in einer Zwischenvariable, oder du deklarierst den Zeiger
gleich auf passende Größe.

Wobei sich natürlich die generelle Frage stellt, warum du den
32-Bit-Prozessor mit einer Ladung an Maskierungsoperationen 
beschäftigst,
damit der dir eine kastrierte 16-Bit-Prüfsumme ausrechnet.  Deklarier'
doch den Zeiger als auf eine 32-Bit-Zahl zeigend und addiere dann
fortlaufend je vier Byte in der Prüfsumme.  Das ist das, was der
Prozessor sowieso am schnellsten kann.

Wobei^2 sich natürlich die Frage stellt, wofür eine simple Prüfsumme
gut ist … CRC wäre besser.

von Frickelfritze (Gast)


Lesenswert?

Erleuchtung, danke sehr.

Kann ich dann dem Compiler helfen (Speicherzugriffe sparen) indem ich
meine(n) 8-bit Buffer immer auf 32 bit "aligne".

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


Lesenswert?

Frickelfritze schrieb:
> Kann ich dann dem Compiler helfen (Speicherzugriffe sparen) indem ich
> meine(n) 8-bit Buffer immer auf 32 bit "aligne".

Ja, wenn du ihn 32bittig zugreifen willst, auf jeden Fall.

Aber wie schon geschrieben, wenn das wirklich 8-Bit-Daten sind,
dann nimm doch lieber eine CRC zur Prüfung.  Die arbeitet auch mit
einzelnen Bytes, kann wahlweise 8, 16 oder 32 Bit als Ergebnis
liefern, und sie ist deutlich sicherer gegen Doppelbitfehler als
eine simple Prüfsumme.

von Frickelfritze (Gast)


Lesenswert?

Jörg W. schrieb:
> damit der dir eine kastrierte 16-Bit-Prüfsumme ausrechnet.  Deklarier'
> doch den Zeiger als auf eine 32-Bit-Zahl zeigend und addiere dann
> fortlaufend je vier Byte in der Prüfsumme.  Das ist das, was der
> Prozessor sowieso am schnellsten kann.

Das geht nicht da die vorgegebenen Datenstrukturen auf 16 Bit
Daten quantisiert sind, als z.B. 6 Byte lang sein können.
Leider ist die geerbte Software so gestaltet dass alles in
8-bit Buffern gehandled wird.

Jörg W. schrieb:
> Wobei^2 sich natürlich die Frage stellt, wofür eine simple Prüfsumme
> gut ist … CRC wäre besser.

Die Strukturen dazu sind gewachsen und können nicht verändert werden.
Siehe vorher ....

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


Lesenswert?

Frickelfritze schrieb:
> Das geht nicht da die vorgegebenen Datenstrukturen auf 16 Bit
> Daten quantisiert sind, als z.B. 6 Byte lang sein können.

Dann sollten sie auf jeden Fall auch auf 16-Bit-Grenzen ausgerichtet
werden.

von Frickelfritze (Gast)


Lesenswert?

Frickelfritze schrieb:
> Leider ist die geerbte Software so gestaltet dass alles in
> 8-bit Buffern gehandled wird.

Man muss auch auf 8-bit Elemente im Detail zugreifen können.

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


Lesenswert?

Frickelfritze schrieb:
> Man muss auch auf 8-bit Elemente im Detail zugreifen können.

Zugriff auf 8-Bit-Elemente bei auf 16 Bit ausgerichteten Daten ist
nie ein Problem.

von Frickelfritze (Gast)


Lesenswert?

Jörg W. schrieb:
> Frickelfritze schrieb:
>> Kann ich dann dem Compiler helfen (Speicherzugriffe sparen) indem ich
>> meine(n) 8-bit Buffer immer auf 32 bit "aligne".
>
> Ja, wenn du ihn 32bittig zugreifen willst, auf jeden Fall.

Nach einiger Überlegung .....
... bewirkt der Cast von 8 auf 16 Bit aber dass der Compiler auf
jeden Fall den Einzelzugriff mit 2 mal 8 bit generieren wird da
er ja nicht von vorne herein weiss ob die Adresse die der Funktion
übergeben worden ist, entsprechend aligned ist oder nicht.

Somit hilft das vorgreifende alignen eines Buffers nichts um den
Zugriff zu optimieren bzw zu beschleunigen.

Gegenargumente?

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


Lesenswert?

Frickelfritze schrieb:
> Nach einiger Überlegung .....
> ... bewirkt der Cast von 8 auf 16 Bit aber dass der Compiler auf
> jeden Fall den Einzelzugriff mit 2 mal 8 bit generieren wird da
> er ja nicht von vorne herein weiss ob die Adresse die der Funktion
> übergeben worden ist, entsprechend aligned ist oder nicht.

Nö.  Er hat dich ja gewarnt, dass er jetzt 16-Bit-Zugriffe macht.

Was dein Prozessor dann daraus macht, ist eine Konfigurationsfrage.
Entweder ein Trap (usage fault) oder ein langsamer Zugriff.

von Trap (Gast)


Lesenswert?

Frickelfritze schrieb:
> Nach einiger Überlegung .....
> ... bewirkt der Cast von 8 auf 16 Bit aber dass der Compiler auf
> jeden Fall den Einzelzugriff mit 2 mal 8 bit generieren wird da
> er ja nicht von vorne herein weiss ob die Adresse die der Funktion
> übergeben worden ist, entsprechend aligned ist oder nicht.
>
> Somit hilft das vorgreifende alignen eines Buffers nichts um den
> Zugriff zu optimieren bzw zu beschleunigen.
>
> Gegenargumente?

Wenn deine Buffer aligned sind wird der Zugriff schneller. Der
Cortex-M3 löst das unaligned Problem transparent im Hintergrund
und splittet einen 16Bit Zugriff  auf 2 8Bit Zugriffe auf dem Bus.

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


Lesenswert?

Trap schrieb:
> Der Cortex-M3 löst das unaligned Problem transparent im Hintergrund

Optional.  Man kann den Core auch so konfigurieren, dass er einen
Usage Fault wirft, indem man das UNALIGN_TRP im SCB_CCR setzt.
Zumindest für die Entwicklung (Debugging und Stresstest) ist das
eigentlich eine gute Idee, das immer zu setzen.

von Frickelfritze (Gast)


Lesenswert?

Trap schrieb:
> Wenn deine Buffer aligned sind wird der Zugriff schneller. Der
> Cortex-M3 löst das unaligned Problem transparent im Hintergrund
> und splittet einen 16Bit Zugriff  auf 2 8Bit Zugriffe auf dem Bus.

Tolle Sache wenn der Prozessor das "at runtime" unterscheiden kann.
Hätte ich ihm jetzt nicht zugetraut.

Wofür aber ist dann so ein "Usage Fault" wenn er die Zugriffe
sowieso meistern kann und kein Fehler für den User zustande kommt?

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


Lesenswert?

Frickelfritze schrieb:
> Wofür aber ist dann so ein "Usage Fault" wenn er die Zugriffe
> sowieso meistern kann und kein Fehler für den User zustande kommt?

Weil es eben ein nicht optimaler Zugriff ist.  Normalweise möchte man
doch Code haben, der so schnell wie möglich abläuft, und das setzt
sauber ausgerichtete Zugriffe auf den Speicher voraus.

von Klaus (Gast)


Lesenswert?

Ich mag ja falsch liegen und irgendwas völlig missverstanden haben, aber 
...

Warum so kompliziert von hinten durchs Auge?

Vielleicht ist es so:
1
uint16_t  CalculateChecksum (uint8_t *pFrom, uint16_t BlockSize)
2
{
3
    uint16_t  nChecksum = 0;
4
    uint16_t  nIndex;
5
6
    for (nIndex = 0; nIndex < BlockSize; nIndex += 2)
7
    {
8
        nChecksum += *(ptr16 + nIndex) << 8;
9
        nChecksum += *(ptr16 + nIndex + 1)
10
    }
11
    return nChecksum;
12
}

klarer und vermeidet auch das alignment Geraffel auf jeder möglichen 
Architektur.

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


Lesenswert?

Klaus schrieb:
> nChecksum += *(ptr16 + nIndex) << 8;
>         nChecksum += *(ptr16 + nIndex + 1);

Oder:
1
        nChecksum += ptr16[nIndex] << 8;
2
        nChecksum += ptr16[nIndex + 1];

Gibt es einen Grund, aufwändig die Zeiger-Dereferenzierung zu klammern,
wenn man es auch als Array-Zugriff schreiben kann?

von Klaus (Gast)


Lesenswert?

Jörg W. schrieb:
> Klaus schrieb:
>> nChecksum += *(ptr16 + nIndex) << 8;
>>         nChecksum += *(ptr16 + nIndex + 1);
>
> Oder:
>
>
1
>         nChecksum += ptr16[nIndex] << 8;
2
>         nChecksum += ptr16[nIndex + 1];
3
>
>
> Gibt es einen Grund, aufwändig die Zeiger-Dereferenzierung zu klammern,
> wenn man es auch als Array-Zugriff schreiben kann?

Keine wirklich akzeptablen Grund. Die Klammern standen halt schon da und 
ich war zu faul, diese Zeile auch noch umzustellen. :-)

von Frickelfritze (Gast)


Lesenswert?

Klaus schrieb:
> Warum so kompliziert von hinten durchs Auge?

Weil mancher Debugger keine Ausdrücke berechnen kann und
man beim Debuggen dann blind ist. Zwischenergebnisse sind
da immer nützlich.

Dann lieber straight forward und hausbacken ....

Es kommt in dieser Funktion auch nicht auf Geschwindigkeit
an. Mich interessierte nur die seltsame Warnung.

Danke für die Erklärungen.

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


Lesenswert?

Frickelfritze schrieb:
> Zwischenergebnisse sind
> da immer nützlich.

Die fliegen aber normalerweise durch den Compiler raus.

von Klaus (Gast)


Lesenswert?

Frickelfritze schrieb:
> Klaus schrieb:
>> Warum so kompliziert von hinten durchs Auge?
>
> Weil mancher Debugger keine Ausdrücke berechnen kann und
> man beim Debuggen dann blind ist. Zwischenergebnisse sind
> da immer nützlich.
>
Die sind ja durchaus erkennbar. Guck Dir einfach die Checksumme an. Und 
falls Du es explizit haben willst, dann:
1
uint16_t  CalculateChecksum (uint8_t *pFrom, uint16_t BlockSize)
2
{
3
    uint16_t nChecksum = 0;
4
    uint16_t nIntermediateChecksum = 0;
5
    uint16_t nIndex;
6
7
    for (nIndex = 0; nIndex < BlockSize; nIndex += 2)
8
    {
9
        nIntermediateChecksum = ptr16[nIndex] << 8;
10
        nIntermediateChecksum += ptr16[nIndex + 1];
11
        nChecksum += nIntermediateChecksum;
12
        nIntermediateChecksum = 0;
13
    }
14
    return nChecksum;
15
}

> Dann lieber straight forward und hausbacken ....

Tut mir leid, aber dein Code ist eben nicht "straight forward" und schon 
gar nicht hausbacken. Schon das alignment Problem ist ein Zeichen dafür.

Auf der ideellen Maschine (im Standard steht, glaube ich, ein anderer 
Begriff aber ich bin immer noch zu faul) gibt es sowas nämlich nicht. 
Und wenn der Compiler warnen muss, dann ist das ein Zeichen, dass die 
Entfernung zwischen der Anwendung der ideellen und der realen Maschine 
merklich ist.

Verstehe mich recht. Mache es wie Du willst, aber Deine Begründung ist, 
meiner Ansicht nach, sachlich nicht zutreffend.

> Es kommt in dieser Funktion auch nicht auf Geschwindigkeit
> an.

Mit Geschwindigkeit hat das nichts zu tun. Es mag evtl. einen 
Unterschied zwischen den Codevarianten geben, aber ich vermute, er ist 
nicht wesentlich.

>  Mich interessierte nur die seltsame Warnung.
> Danke für die Erklärungen.

Gerne.

von Steffen R. (steffen_rose)


Lesenswert?

Jörg W. schrieb:
> Trap schrieb:
>> Der Cortex-M3 löst das unaligned Problem transparent im Hintergrund
>
> Optional.  Man kann den Core auch so konfigurieren, dass er einen
> Usage Fault wirft, indem man das UNALIGN_TRP im SCB_CCR setzt.
> Zumindest für die Entwicklung (Debugging und Stresstest) ist das
> eigentlich eine gute Idee, das immer zu setzen.

Atmel SAM3X:

Unaligned LDM, STM, LDRD, and STRD instructions always fault 
irrespective of whether UNALIGN_TRP is set to 1.

Will man unaligned Zugriffe ausnutzen, muss man die Benutzung dieser 
Befehle abschalten. Weiß nicht, ob dies beim gcc möglich ist.

Sollte aber gehen, sobald man den Pointer auf __packed deklariert. Dann 
werden jedoch explizit 8bit Befehle genutzt und aufwendig 
zusammengesetzt.

Nur mal als weiterer Denkanstoß. Die vorgenannten Infos beziehen sich ja 
auf notwendige Randbedingungen für eine höhere Effektivität. Da aber 
eine gewachsene Software im Gespräch war, ist dies möglicherweise nicht 
realisierbar.

von Eric B. (beric)


Lesenswert?

Jörg W. schrieb:
> Gibt es einen Grund, aufwändig die Zeiger-Dereferenzierung zu klammern,
> wenn man es auch als Array-Zugriff schreiben kann?

Ja, MISRA. MISRA mag keine Pointer indizieren.
1
uint8_t an_array[] = { 1, 2 ,3 };
2
uint8_t *a_pointer;
3
...
4
j = an_array[i];      // OK
5
j = *(an_array + i);  // Hier meckert MISRA
6
...
7
j = a_pointer[i];     // Hier meckert MISRA
8
j = *(a_pointer + i); // OK

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


Lesenswert?

Eric B. schrieb:
> MISRA mag keine Pointer indizieren.

Manche Sachen an MISRA muss man wohl nicht verstehen.

Wenn ich ein Array an eine Funktion übergebe, wird bekanntlich ein
Zeiger übergeben.  Den darf ich dann in der aufgerufenen Funktion
aber nicht mehr wie ein Array behandeln.

Naja, es gibt Sachen an MISRA, die sinnvoll sind, aber manche der
Begründungen, die ich da gelesen habe, erscheinen eher an den Haaren
herbeigezogen.

von Klaus (Gast)


Lesenswert?

Wenn auch halb scherzhaft, so meine ich, das Argumente, die sich auf 
MISRA berufen (so MISRA nicht selbst das Thema ist) hier vermieden 
werden sollte.

Das ist die P... im A... Die Stein gewordene Sorgenfalte irgendeines 
Buchhalters. Scheisslich.

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.