Hallo zusammen,
ich habe ein Problem, dass ich anhand eines Beispiels erläutern möchte
(Hinweis vorab: Ich arbeite mit einem XScale - Prozessor (PXA255) und
gcc 4.1.2):
#include <stdio.h>
[...]
typedef struct
{
Enum8 bla;
DInt diValue;
Real rValue;
} _attribute_ ((packed)) Test_Struct;
//} Test_Struct;
Test_Struct TestVar[3];
int main ()
{
Real *rp;
rp = &TestVar[1].rValue;
printf ("rValue = %f\n", TestVar[1].rValue);
printf ("diValue = %ld\n", TestVar[1].diValue);
printf("fp_Adresse: %p\n",rp);
*f_p = 5.678;
printf ("rValue = %f\n", TestVar[1].rValue);
printf ("diValue = %ld\n", TestVar[1].diValue);
return (1);
}
Ein float-Pointer wird angelegt und zeigt auf ein Strukturelement
(rValue). Nun möchte ich per Zuweisung in den Speicher, auf den f_p
zeigt den Wert 5,678 reinschreiben. Das geht aber nur dann gut, wenn die
oben deklarierte Struktur nicht mit _attribute_ ((packed)) angelegt
wird. Klar ist, dass f_p auf eine ungerade Adresse zeigt. Bei der
Zuweisung wird in den Speicherbereich vom diValue reingeschrieben (weil
hier die nächsthöhere gerade Adresse ist). Meine Fragen:
1. Ist das ein Prozessor- oder ein Compilerproblem?
2. Wie kann ich das Problem umgehen bzw. beheben?
Ich habe hier im Forum schon gesucht, die Antworten, die jeweils gegeben
wurden haben mir nicht wirklich weiter geholfen.
Danke schonmal & Gruß, Bierbauch
> 1. Ist das ein Prozessor- oder ein Compilerproblem?
Es ist eher ein Problem deines Programms bzw. deiner Erwartung, daß der
Prozessor mit Daten umgehen kann, die mit falschem Alignment im Speicher
stehen. Manche Prozessoren unterstützen das bei reduzierter Perfomance,
andere gar nicht. Generell sollte man es vermeiden.
> 2. Wie kann ich das Problem umgehen bzw. beheben?
Dem Compiler das Alignment überlassen, indem du das Attribut einfach
wegläßt.
bierbauch wrote:
> typedef struct> {> Enum8 bla;> DInt diValue;> Real rValue;> } _attribute_ ((packed)) Test_Struct;> //} Test_Struct;>> Test_Struct TestVar[3];> rp = &TestVar[1].rValue;> 1. Ist das ein Prozessor- oder ein Compilerproblem?
Üblicherweise macht sich ein Compiler nicht den Wolf, unalignten Zugriff
zu unterstützen, wenn die zugrundeliegende Architektur das nicht kann.
> 2. Wie kann ich das Problem umgehen bzw. beheben?
Falls du nicht umcodieren kannst zu vernünftigen Alignments, kann über
eine Union gelesen werden:
1
union
2
{
3
unsignedcharasByte[4];
4
floatasFloat;
5
}foo;
Aber Vorsich:
-- Falls der µC spezielle FP-regs hat, kann das die Darstellung
korrumpieren (automatische Konvertierung/Rundung durch die Hardware,
wenn ein Wert in ein fp-reg geschoben wird).
-- strict aliasing beachten
Johann
Versuch mal, den Float an den Anfang der Struktur zu legen. Der Enum
scheint ja das Misalignment reinzubringen. Aber Vorsicht: Das ist ein
Hack, der nur dann funktionieren kann, wenn die Struktur als Ganzes
wieder aligned ist.
Generell gilt, was Rolf Magnus gesagt hat: Wenn es die CPU nicht
unterstützt, dann lass' es weg. Durch eine entsprechende Sortierung
kannst Du zumindest die Padding bytes umgehen. Ob das vom Design der
Struktur dann noch Sinn macht, musst Du selbst entscheiden.
Ich würde immer ein gutes Design favorisieren.
Rolf Magnus wrote:
>> 1. Ist das ein Prozessor- oder ein Compilerproblem?>> Es ist eher ein Problem deines Programms bzw. deiner Erwartung, daß der> Prozessor mit Daten umgehen kann, die mit falschem Alignment im Speicher> stehen.
Johann L. wrote:
> Üblicherweise macht sich ein Compiler nicht den Wolf, unalignten Zugriff> zu unterstützen, wenn die zugrundeliegende Architektur das nicht kann.
Mit Verlaub, das ist Quatsch. Wenn mir ein Compiler die Möglichkeit
gibt, Daten explizit als unaligned zu deklarieren, dann erwarte ich
entweder
1. dass der Compiler das unabhängig von der Zielarchitektur auch
hinbekommt. (bevorzugtes Verhalten)
2. dass der Compiler mir eine Fehlermeldung ausgibt, falls die
Zielarchitektur unaligned Daten nicht unterstützt und der Compiler
sich ebenfalls nicht darum kümmern möchte.
Jedes andere Verhalten ist ein Bug des Compilers. Wir hatten hier vor
nicht allzulanger Zeit einen ähnlichen Thread, in dem wir das alles
schon Mal durchgekaut haben.
Mein Tip: Versuch's mal mit dem GCC für ARM von CodeSourcery.
Gruß
Marcus
http://www.doulos.com/arm/
Marcus Harnisch wrote:
> Rolf Magnus wrote:>>> 1. Ist das ein Prozessor- oder ein Compilerproblem?>>>> Es ist eher ein Problem deines Programms bzw. deiner Erwartung, daß der>> Prozessor mit Daten umgehen kann, die mit falschem Alignment im Speicher>> stehen.>> Johann L. wrote:>> Üblicherweise macht sich ein Compiler nicht den Wolf, unalignten Zugriff>> zu unterstützen, wenn die zugrundeliegende Architektur das nicht kann.>> Mit Verlaub, das ist Quatsch. Wenn mir ein Compiler die Möglichkeit> gibt, Daten explizit als unaligned zu deklarieren, dann erwarte ich> entweder>> 1. dass der Compiler das unabhängig von der Zielarchitektur auch> hinbekommt. (bevorzugtes Verhalten)>> 2. dass der Compiler mir eine Fehlermeldung ausgibt, falls die> Zielarchitektur unaligned Daten nicht unterstützt und der Compiler> sich ebenfalls nicht darum kümmern möchte.>> Jedes andere Verhalten ist ein Bug des Compilers. Wir hatten hier vor> nicht allzulanger Zeit einen ähnlichen Thread, in dem wir das alles> schon Mal durchgekaut haben.
Nehmen wir mal an, du hast einen long * p, also einen Zeiger auf einen
32-Bit-Wert. Nehmen wir weiterhin an, daß die Architektur nur alignten
Zugriff unterstützt. Beachte, daß in long keine Information über das
Aligment transportier wird (packed ist ein Attribut, kein Qualifier!),
d.h. man müsste Code erzeugen, der zur Laufzeit unterscheidet, ob p
aligned ist oder nicht (Oder immer Code erzeugen, der unalignten Zugriff
unterstützt). Oder seh ich das falsch? Und zwar für jeden solchen
Zugriff. Wenn der Zugriff über ein Symbol erfolgt ist klar, wie es zu
geschehen hat (sagt die ABI oder EABI), weil da Attribute dran kleben.
Wie soll ein Compiler jedoch long a = *(long*) b); implementieren?
So, daß es für jeden unalignten Pointer geht, und alle Anwender auf dem
Matte stehen, und über den langsamen und breiten und nicht-atomaren Code
jammern?
Oder das, was die Architektur unterstützt. Zudem ist sowas wie gesagt in
einer ABI oder EABI beschrieben (oder sollte es zumindest), die mir für
diese Architektur zugegebenermassen nicht vertraut ist, und sich von
Compiler zu Compiler unterscheiden kann -- auch zwischen
unterschiedlichen gcc-Ports für die gleiche Architektur.
Johann
Johann L. wrote:
> Nehmen wir mal an, du hast einen long * p, also einen Zeiger auf einen> 32-Bit-Wert. Nehmen wir weiterhin an, daß die Architektur nur alignten> Zugriff unterstützt. Beachte, daß in long keine Information über das> Aligment transportier wird (packed ist ein Attribut, kein> Qualifier!),
In "long" nicht, aber in der Typdeklaration von Test_Struct.
> d.h. man müsste Code erzeugen, der zur Laufzeit unterscheidet, ob p> aligned ist oder nicht (Oder immer Code erzeugen, der unalignten Zugriff> unterstützt).
Das müsste man nur, wenn der Programmierer absichtlich (type casts,
siehe unten) versucht, den Compiler daran zu hindern, das alignment
anhand der Deklarationen zu erkennen.
> Wie soll ein Compiler jedoch long a = *(long*) b); implementieren?
Wie gesagt, selbst schuld. Ein
1
typedeflongp_long__attribute__((packed));
2
3
p_longa=*(p_long*)b;
hätte vermutlich schon geholfen.
> So, daß es für jeden unalignten Pointer geht, und alle Anwender auf dem> Matte stehen, und über den langsamen und breiten und nicht-atomaren Code> jammern?
Man muss sich natürlich über die Auswirkungen der Verwendung solcher
Konstrukte bewusst sein. Aber die von mir zitierten Aussagen sind
schlichtweg falsch.
Gruß
Marcus
http://www.doulos.com/arm/
Marcus Harnisch wrote:
> Johann L. wrote:>> Nehmen wir mal an, du hast einen long * p, also einen Zeiger auf einen>> 32-Bit-Wert. Nehmen wir weiterhin an, daß die Architektur nur alignten>> Zugriff unterstützt. Beachte, daß in long keine Information über das>> Aligment transportier wird (packed ist ein Attribut, kein>> Qualifier!),>> In "long" nicht, aber in der Typdeklaration von Test_Struct.
Welchen Typs ist & Test_Struct.rValue ?
> Das müsste man nur, wenn der Programmierer absichtlich (type casts,> siehe unten) versucht, den Compiler daran zu hindern, das alignment> anhand der Deklarationen zu erkennen.>>> Wie soll ein Compiler jedoch long a = *(long*) b); implementieren?>> Wie gesagt, selbst schuld. Ein
Nein, damit wollte ich nich ein Cast-Hacking ausdrücken, sondern
Zugriffe wie in
1
longfoo(long*b)
2
{
3
returna;
4
}
>
1
>typedeflongp_long__attribute__((packed));
2
>
3
>p_longa=*(p_long*)b;
4
>
Ein gepackter long? Mal vorausgesetzt, daß men dem ne sinnvolle Semantik
zuordnet (zB daß ein unalignter Zugriff erfolgen soll): Was ist dann der
Unterschied zwischen
Attribute sind keine Qualifier, d.h. sowas ist nicht ausdrückbar und es
ist nicht konsistent unterstützbar. Jedenfalls nicht so wie andere
Qualifier die Zugriffe regeln wie etwa volatile. Wenn du das Attribut
durch einen Qualifier ersetzt, ergeben alle diese Definitionen
unterschiedliche Typen (zumindest im gcc). Für Attribute ist das nicht
der Fall.
Ab irgendeiner Ebene fliegt's dir also um die Ohren, da wäre genau so,
als könnte man nicht unterscheiden zwischen
Interessant... Aber wir schweifen ab. Mein Einwand bezog sich lediglich
auf die Aussage, dass Compiler unaligned Zugriffe auf bestimmten
Architekturen (wie, z.B. ARM <v6) nicht richtig unterstützen. Sie tun es
(abgesehen von einigen GCC bugs, siehe anderer Thread), aber hellsehen
kann der Compiler natürlich nicht.
Johann L. wrote:
> Ein gepackter long? Mal vorausgesetzt, daß men dem ne sinnvolle Semantik> zuordnet
DU wolltest ihn doch packen :-)
> long ** __attribute__((packed)) p;> long * __attribute__((packed)) * p;> long __attribute__((packed)) p;> long __attribute__((packed)) * __attribute__((packed)) * p;> Attribute sind keine Qualifier, d.h. sowas ist nicht ausdrückbar und es> ist nicht konsistent unterstützbar.
Habe leider gerade keine Zeit, das nachzuprüfen. Nebenbei, im RealView
Compiler gibt es den (ebenfalls nicht standard konformen) Qualifier
__packed.
Gruß
Marcus
http://www.doulos.com/arm/
Das attribute ((packed)) ist für die Fälle da, wo man Binärdateien von
einem anderen System weiterverarbeiten muss.
Zum Weiterverarbeiten muss dann jedes Element in eine "normale" Struktur
kopieren. Das darf dann auch keine Zuweisung sein, sondern memcpy.
Hans-jürgen Herbert wrote:
> Zum Weiterverarbeiten muss dann jedes Element in eine "normale" Struktur> kopieren.
Nein, man "muss" nicht. Kann sich je nach Anwendung aber lohnen. Die
Zugriffe auf unaligned data sind bei ARM Architekturen <6 nicht optimal.
Selbst bei den neueren Prozessoren hat man die zusätzlichen Buszyklen.
Letzteres ist aber nicht ARM-spezifisch.
Bei häufigem Zugriff auf die Daten kann die Ersparnis den Aufwand für
das Kopieren der Daten aufwiegen.
> Das darf dann auch keine Zuweisung sein, sondern memcpy.
Das darf selbstverständlich auch eine Zuweisung sein.
Gruß
Marcus
http://www.doulos.com/arm/
> Die Zugriffe auf unaligned data sind bei ARM Architekturen <6 nicht> optimal.
Und bei größeren dann gar nicht möglich - zumindest bei dem ARM7, mit
dem ich arbeite.
> Selbst bei den neueren Prozessoren hat man die zusätzlichen> Buszyklen.
Nach meiner Erfahrung ist die Zahl der Prozessoren, bei denen das so
ist, geringer, als die, bei denen es gar nicht geht.
> Das darf dann auch keine Zuweisung sein, sondern memcpy.>> Das darf selbstverständlich auch eine Zuweisung sein.
Ist halt genauso wie bei jedem anderen Zugriff. Wenn das bei Daten mit
falschem Alignment nicht geht, geht auch die Zuweisung nicht.
Rolf Magnus wrote:
>> Die Zugriffe auf unaligned data sind bei ARM Architekturen <6 nicht>> optimal.>> Und bei größeren dann gar nicht möglich - zumindest bei dem ARM7, mit> dem ich arbeite.
Aber ein ARM7 (bzw. ARM7TDMI) implementiert die Architektur 7T, ist
also <6. Zugegebenermaßen etwas verwirrend. Seit ARM11 (6) wird
unaligned Zugriff durch die Hardware unterstützt.
>> Selbst bei den neueren Prozessoren hat man die zusätzlichen>> Buszyklen.>> Nach meiner Erfahrung ist die Zahl der Prozessoren, bei denen das so> ist, geringer, als die, bei denen es gar nicht geht.
Wenn ich beispielsweise einen 32bit Datenbus habe, und mein 32bit Wert
an Adresse 1 beginnt, dann benötige ich wenigstens zwei Buszyklen,
oder nicht?
> Ist halt genauso wie bei jedem anderen Zugriff. Wenn das bei Daten mit> falschem Alignment nicht geht, geht auch die Zuweisung nicht.
So formuliert hast Du natürlich recht. Aber das Alignment ist ja nicht
in dem Sinne falsch. Es entspricht nur nicht dem Alignment der
Datenzugriffe des Prozessors. Jetzt ist es am Compiler, sich die Daten
durch Byte/Halbwort Zugriffe zusammenzubasteln.
Ich habe mal versucht, das mit ähnlichem Code wie dem des OP
darzustellen. Das Programm läuft wie erwartet und die Ausgabe ist in
beiden Fällen identisch.
Eine Struktur ist aligned, die andere nicht. Die zweite (unaligned)
wird initialisiert und die Elemente werden durch Zuweisung in die
andere Struktur (aligned) kopiert.
Gruß
Marcus
http://www.doulos.com/arm/
> Aber ein ARM7 (bzw. ARM7TDMI) implementiert die Architektur 7T, ist> also <6.
Ach so.
> Wenn ich beispielsweise einen 32bit Datenbus habe, und mein 32bit> Wert an Adresse 1 beginnt, dann benötige ich wenigstens zwei> Buszyklen, oder nicht?
Das nehme ich an.
> So formuliert hast Du natürlich recht. Aber das Alignment ist ja nicht> in dem Sinne falsch. Es entspricht nur nicht dem Alignment der> Datenzugriffe des Prozessors.
Das ist Ansichtssache.
> Jetzt ist es am Compiler, sich die Daten durch Byte/Halbwort Zugriffe> zusammenzubasteln.
Nein, denn der Compiler selbst kann nur in Spezialfällen wissen, ob das
Alingment paßt. Damit das allgemein funktioniert, müßte er für so
ziemlich jeden Zugriff Code einbauen, der das zur Laufzeit macht. Das
würde einen ziemlichen Overhead bezüglich Codegröße und Laufzeit
bedeuten, auch für Zugriffe mit passendem Alignment.
> Eine Struktur ist aligned, die andere nicht. Die zweite (unaligned)> wird initialisiert und die Elemente werden durch Zuweisung in die> andere Struktur (aligned) kopiert.
Ja, wenn der Prozessor das unterstützt, geht das. Wenn der Code eh nicht
portabel sein soll, kann man das wohl so machen. Allgemein würde ich
trotzdem lieber darauf verzichten.
Rolf Magnus wrote:
> Marcus Harnisch wrote:>> Aber ein ARM7 (bzw. ARM7TDMI) implementiert die Architektur 7T, ist>> also <6.
Au weia. Das war natürlich der ungünstigste Ort für einen
Tippfehler. Der Satz sollte lauten:
>> Aber ein ARM7 (bzw. ARM7TDMI) implementiert die Architektur 4T, ist>> also <6.>> Jetzt ist es am Compiler, sich die Daten durch Byte/Halbwort Zugriffe>> zusammenzubasteln.>> Nein, denn der Compiler selbst kann nur in Spezialfällen wissen, ob das> Alingment paßt.
Wie z.B. duch Angabe von
1
__attribute__((packed))
> Damit das allgemein funktioniert, müßte er für so ziemlich jeden> Zugriff Code einbauen, der das zur Laufzeit macht. Das würde einen> ziemlichen Overhead bezüglich Codegröße und Laufzeit bedeuten, auch> für Zugriffe mit passendem Alignment.
Genau. Man würde die mehrfach benötigten Teile der Struktur in lokale
Variablen kopieren, und/oder darauf vertrauen, dass der Compiler die
Registerverwaltung beherrscht. Bei CPUs mit relativ vielen Registern
wird ja der Zugriff auf die Struktur nicht jedes Mal neu ausgeführt.
Gruß
Marcus
http://www.doulos.com/arm/