Forum: Compiler & IDEs MinGW: __attribute__((packed)) und alignment


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,

ich habe ein struct definiert mit:
1
__extension__ typedef struct 
2
{
3
    uint16_t val0: 10; 
4
    uint16_t val1: 10;
5
    uint8_t crc : 4; 
6
} __attribute__((packed)) Eedata_t;

und baue das Ganze unter MinGW64 (eine GCC-Ausprägung für Windows). 
Sizeof von Eedata_t liefert 5 - d.h. der Compiler mag das struct nicht 
packen, sondern richtet es an 16-Bit-Grenzen aus.

Gibt es eine Möglichkeit, dem Compiler beizubiegen, ohne Rücksicht auf 
Optimierungsverluste und nötigen Schiebeoperationen das Ganze auf 24 Bit 
zu packen oder muß ich in den sauren Apfel beißen und von Hand schieben?


Viele Grüße
W.T.

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


Lesenswert?

Seltsam:
1
$ cat foo.c
2
#include <stdint.h>
3
4
__extension__ typedef struct 
5
{
6
    uint16_t val0: 10; 
7
    uint16_t val1: 10;
8
    uint8_t crc : 4; 
9
} __attribute__((packed)) Eedata_t;
10
11
Eedata_t foo;
12
13
int getval1(Eedata_t *p)
14
{
15
   return p->val1;
16
}
17
$ cc -O -S foo.c
18
$ cat foo.s
19
        .file   "foo.c"
20
        .text
21
        .globl  getval1
22
        .type   getval1, @function
23
getval1:
24
.LFB0:
25
        .cfi_startproc
26
        movzbl  1(%rdi), %edx
27
        shrb    $2, %dl
28
        movzbl  %dl, %edx
29
        movzbl  2(%rdi), %eax
30
        andl    $15, %eax
31
        salq    $6, %rax
32
        orl     %edx, %eax
33
        ret
34
        .cfi_endproc
35
.LFE0:
36
        .size   getval1, .-getval1
37
        .comm   foo,3,1
38
        .ident  "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4"
39
        .section        .note.GNU-stack,"",@progbits

Kann mir nicht vorstellen, dass sich ein MinGW32 anders verhält als
der Compiler hier unter Linux.

von Walter T. (nicolas)


Lesenswert?

Jörg W. schrieb:
> Kann mir nicht vorstellen, dass sich ein MinGW32 anders verhält als
> der Compiler hier unter Linux.

Schwer zu sagen: Ich nutze und Windows MinGW64 und unter Linux Clang64. 
Aber ich baue mal ein funktionierendes Minimalbeispiel.

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


Lesenswert?

Mit Clang auf FreeBSD war der generierte Code etwas umständlicher,
aber dennoch klar und deutlich gepackt auf 3 Bytes.

Wie sieht denn obiges Minimalbeispiel mit gleichen Compileroptionen
bei dir unter MinGW64 aus?

von Walter T. (nicolas)


Lesenswert?

OK, wir haben uns etwas gedoppelt:

Unter Clang habe ich auch 3 Bytes als Größe (sowohl bei Deinem als auch 
meinem Minimalbeispiel).

Bei Deinem Minimalbeispiel unter MinGW64:
1
  .file  "foo.c"
2
  .text
3
  .globl  getval1
4
  .def  getval1;  .scl  2;  .type  32;  .endef
5
  .seh_proc  getval1
6
getval1:
7
  .seh_endprologue
8
  movzbl  2(%rcx), %edx
9
  movzbl  3(%rcx), %eax
10
  andl  $3, %eax
11
  salq  $8, %rax
12
  orl  %edx, %eax
13
  ret
14
  .seh_endproc
15
  .comm  foo, 5, 0
16
  .ident  "GCC: (tdm64-1) 5.1.0"

Für mich ist X64/X86er-Asm kryptisch, aber auf den ersten Blick sieht 
das nach einem anderen Offset aus.

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


Lesenswert?

Walter T. schrieb:
> Für mich ist X64/X86er-Asm kryptisch, aber auf den ersten Blick sieht
> das nach einem anderen Offset aus.

Ja, insbesondere hast du da "comm foo, 5, 0" stehen, also 5 Bytes.

Sehr seltsam.  OK, ist natürlich ein GCC 5.x, habe ich hier gerade
nicht zur Hand.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

uint16_t etc. als Basis-Typ für Bitfeld ist ja implementation defined 
bzw. deren Unterstützung ist noch nicht einmal vom Standard 
vorgeschrieben.  Gibt es mit unsigned das gleiche Ergebnis?  Und was 
passiert mit aligned(1)?

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


Lesenswert?

Johann L. schrieb:
> Gibt es mit unsigned das gleiche Ergebnis?

Bzw. unsigned char / unsigned short?

von Walter T. (nicolas)


Lesenswert?

Jörg W. schrieb:
> Johann L. schrieb:
>> Gibt es mit unsigned das gleiche Ergebnis?
>
> Bzw. unsigned char / unsigned short?

"unsigned" bringt noch eine neue Variante ins Spiel:
1
  .file  "foo.c"
2
  .text
3
  .globl  getval1
4
  .def  getval1;  .scl  2;  .type  32;  .endef
5
  .seh_proc  getval1
6
getval1:
7
  .seh_endprologue
8
  movzbl  1(%rcx), %eax
9
  shrb  $2, %al
10
  movzbl  %al, %edx
11
  movzbl  2(%rcx), %eax
12
  andl  $15, %eax
13
  salq  $6, %rax
14
  orl  %edx, %eax
15
  ret
16
  .seh_endproc
17
  .comm  foo, 4, 0
18
  .ident  "GCC: (tdm64-1) 5.1.0"

bzw. der "sizeof"-Operator liefert 4 zurück.

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


Lesenswert?

Walter T. schrieb:
> der "sizeof"-Operator liefert 4 zurück.

Ja, sagt ja auch die .comm-Anweisung.

von Walter T. (nicolas)


Lesenswert?

Ich hab's produktiv mit Bitschieberei gelöst - auch wenn mir type 
punning über Bitfelder lieber ist (da wartbarer).

Es verbleibt also nur die Neugier, warum GCC 5.1.0 und Clang 
unterschiedlicher Meinung sind, was ihre Interpretation von "packed" 
angeht.

(Ich bitte von Vorschlägen à la "nimm eine andere GCC-Version" 
abzusehen. Diese Distribution mit MinGW64/TDM-GCC arbeitet hervorragend 
als MEX-Compiler mit Matlab zusammen - was andere Versionen leider 
(noch) nicht tun.)

von Adib (Gast)


Lesenswert?

Hallo zusammen,

das Problem kenne ich. Es gab da mal irgendwo ein Hinweis.

Man muss bei dem MinGW Compiler noch folgende Option setzen:
1
-mno-ms-bitfields

Grüße,

Adib.
--

von Walter T. (nicolas)


Lesenswert?

Adib schrieb:
> Man muss bei dem MinGW Compiler noch folgende Option
> setzen:-mno-ms-bitfields

Danke für den Hinweis!

Mit der obengenannten Compileroption bzw. dem Attribut
1
__attribute__((gcc_struct))

kommt das erwartete gepackte struct mit 3 Bytes Breite heraus.

Jetzt kann ich meinen Quelltext wieder Zurück-Ändern.

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.