Quantcast

'struct' member alignment query

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

'struct' member alignment query

Philip Reames via cfe-dev
Our architecture has fairly conventional data layout rules, with:

  o  8-bit, byte aligned 'char'              (int8_t)
  o  16-bit, 2-byte aligned 'short'          (int16_t)
  o  32-bit, 4-byte aligned 'int' and 'long' (int32_t)
  o  64-bit, 8-byte aligned 'long long'      (int64_t)

However, I wanted to experiment with 'struct's that internally have
self-consistent member alignment, but which deliberately map a 'struct'
member of another 'struct' onto a boundary that is not consistent with the
member 'struct's desired alignment.

The test I am using is appended (it is a C file), and the line in this that
I have a question about is #21:

    AlignCheck_t alignCheck2 __attribute__((aligned(1)));  // Should have an
alignment of 1-Byte???

where I have tried to override the default alignment for the aggregate
member, but the '__attribute__((aligned(1)))' does not appear to have any
effect and the member is padded in the normal way, and so fails with the
test at line #64.

Is this expected behaviour in 'clang', and if so, is there another way I can
coerce the 'struct' member 'alignCheck2' onto another less constrained
alignment boundary?

Thanks,

        MartinO

---------- alignmentCheck.c ----------
#include <stdio.h>
#include <stddef.h>

typedef struct AlignCheck_t {
   char  a;         // 0:    [a]
                    // 1:    [X-X-X]   padding
   int   b;         // 4:    [b-b-b-b]
   char  c;         // 8:    [c]
                    // 9:    [X]       padding
   short d;         // 10:   [d-d]
   char  e;         // 12:   [e]
                    // 13:   [X-X-X]   padding
   long long  f[2]; // 16:   [f-f-f-f-f-f-f-f][f-f-f-f-f-f-f-f]
} AlignCheck_t;

typedef struct Group_t {
    char         dummy1;                                   // Should have an
alignment of 1-Byte
    AlignCheck_t alignCheck1;                              // Should have an
alignment of 8-Bytes

    char         dummy2;                                   // Should have an
alignment of 1-Byte
    AlignCheck_t alignCheck2 __attribute__((aligned(1)));  // Should have an
alignment of 1-Byte???

    char         dummy3;                                   // Should have an
alignment of 1-Byte
} Group_t;

char dummy0;                                               // Should have an
alignment of 1-Byte
Group_t alignedGroup = {
                            1,
                           {  2,  3,  4,  5,  6,  7ll },
                           11,
                           { 12, 13, 14, 15, 16, 17ll },
                           21
                       };

int main( void )
{
    int failed = 0;
    unsigned check1 = (unsigned)&alignedGroup.dummy1;
    unsigned check2 = (unsigned)&alignedGroup.alignCheck1;
    unsigned check3 = (unsigned)&alignedGroup.dummy2;
    unsigned check4 = (unsigned)&alignedGroup.alignCheck2;
    unsigned check5 = (unsigned)&alignedGroup.dummy3;

    if (( check2 & 0x00000007 ) != 0 )
        failed = 1;
    else if ( offsetof ( AlignCheck_t, a ) != 0 )
        failed = 2;
    else if ( offsetof ( AlignCheck_t, b ) != 4 )
        failed = 3;
    else if ( offsetof ( AlignCheck_t, c ) != 8 )
        failed = 4;
    else if ( offsetof ( AlignCheck_t, d ) != 10 )
        failed = 5;
    else if ( offsetof ( AlignCheck_t, e ) != 12 )
        failed = 6;
    else if ( offsetof ( AlignCheck_t, f ) != 16 )
        failed = 7;
    else if ( sizeof ( AlignCheck_t ) != 32 )
        failed = 8;
    else if (( check2 - check1 ) != 8 )
        failed = 9;
    else if (( check3 - check2 ) != 32 )
        failed = 10;
    else if (( check4 - check3 ) != 1 )
        failed = 11;
    else if (( check5 - check4 ) != 32 )
        failed = 12;

    if ( failed )
        puts("\n\nAlignment check FAILED\n\n");
    else
        puts("\n\nAlignment check PASSED\n\n");

    return failed;
}


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: 'struct' member alignment query

Philip Reames via cfe-dev
On 9 February 2017 at 02:56, Martin J. O'Riordan via cfe-dev
<[hidden email]> wrote:
> Is this expected behaviour in 'clang', and if so, is there another way I can
> coerce the 'struct' member 'alignCheck2' onto another less constrained
> alignment boundary?

It's expected, but I think only because of GCC compatibility because
it's really not obvious behaviour. The workaround, which still
mystifies me, is to go via a typedef:

typedef AlignCheck_t __attribute__((aligned(1))) UnAlignCheck_t;

then you can put that in the struct and it'll be laid out without any padding.

Tim.
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: 'struct' member alignment query

Philip Reames via cfe-dev
Wow!  Thanks for the response, that worked perfectly.

I never would have expected that the indirect 'typedef' approach would behave any differently to the more direct annotation!  I feel that this is probably an unintended GCC behaviour/bug, though CLang is 100% right to maintain compatibility with GCC.

But it solved my problem, thanks very much,

        MartinO

-----Original Message-----
From: Tim Northover [mailto:[hidden email]]
Sent: 09 February 2017 15:08
To: Martin J. O'Riordan <[hidden email]>
Cc: cfe-dev <[hidden email]>
Subject: Re: [cfe-dev] 'struct' member alignment query

On 9 February 2017 at 02:56, Martin J. O'Riordan via cfe-dev <[hidden email]> wrote:
> Is this expected behaviour in 'clang', and if so, is there another way
> I can coerce the 'struct' member 'alignCheck2' onto another less
> constrained alignment boundary?

It's expected, but I think only because of GCC compatibility because it's really not obvious behaviour. The workaround, which still mystifies me, is to go via a typedef:

typedef AlignCheck_t __attribute__((aligned(1))) UnAlignCheck_t;

then you can put that in the struct and it'll be laid out without any padding.

Tim.

_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: 'struct' member alignment query

Philip Reames via cfe-dev
The "right" way to get this effect is to use __attribute__((packed)) on the field.

On 9 Feb 2017 9:33 am, "Martin J. O'Riordan via cfe-dev" <[hidden email]> wrote:
Wow!  Thanks for the response, that worked perfectly.

I never would have expected that the indirect 'typedef' approach would behave any differently to the more direct annotation!  I feel that this is probably an unintended GCC behaviour/bug, though CLang is 100% right to maintain compatibility with GCC.

But it solved my problem, thanks very much,

        MartinO

-----Original Message-----
From: Tim Northover [mailto:[hidden email]]
Sent: 09 February 2017 15:08
To: Martin J. O'Riordan <[hidden email]>
Cc: cfe-dev <[hidden email]>
Subject: Re: [cfe-dev] 'struct' member alignment query

On 9 February 2017 at 02:56, Martin J. O'Riordan via cfe-dev <[hidden email]> wrote:
> Is this expected behaviour in 'clang', and if so, is there another way
> I can coerce the 'struct' member 'alignCheck2' onto another less
> constrained alignment boundary?

It's expected, but I think only because of GCC compatibility because it's really not obvious behaviour. The workaround, which still mystifies me, is to go via a typedef:

typedef AlignCheck_t __attribute__((aligned(1))) UnAlignCheck_t;

then you can put that in the struct and it'll be laid out without any padding.

Tim.

_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: 'struct' member alignment query

Philip Reames via cfe-dev

Hi Richard,

 

How does ‘packed’ work in this context?  I thought ‘packed’ would cause the whole aggregate to eliminate padding, rather than just changing the alignment requirements of a single member.

 

What I want is that the outer aggregate is aligned in the normal way according the maximum alignment requirements of its members.  However, I want to also have a member which itself is an aggregate with its usual internal padding retained, but changed its alignment constraint (essentially lying) within the context of the enclosing aggregate.

 

Would ‘packed’ not just unilaterally remove the padding?

 

For example (assuming char is 1-byte and int is 4, and natural alignment):

 

struct Inner {

  char c;

  int  i;

};

Inner x;

 

I can reasonably assume on a naturally aligned system that the following would hold true (undefined ISO behaviour ignored regarding address arithmetic - assume a simple conventional 32-bit Von Neumann address space):

 

sizeof(Inner) == 8

((unsigned)&x & 7) == 0

((unsigned)&x.i - (unsigned)&x.c) == 4

offsetof(Outer, c) == 0

offsetof(Outer, i) == 4

 

Now what I want to do is:

 

struct Outer {

  char aa;

  int  bb;

  char cc;

  [magic attribute] Inner xx;

  char dd;

  int  ee;

};

Outer y;

 

Such that:

 

sizeof(Outer) == 24

((unsigned)&y & 7) == 0

((unsigned)&y.xx.i - (unsigned)&y.xx.c) == 4

((unsigned)&y.xx.i - (unsigned)&y) == 13

offsetof(Inner, bb) == 4

offsetof(Inner, cc) == 8

offsetof(Inner, xx) == 9

offsetof(Inner, dd) == 17

offsetof(Inner, ee) == 20

 

so padding is still present within the layout of ‘xx’, and also as normal for the member alignment of ‘aa’, ‘bb’, ‘cc’, ‘dd’ and ‘ee’ within ‘Outer’, but not for ‘xx’.

 

This is useful when overlaying structures on registers in a memory-mapped device which can have weird arrangements, and avoid the need for dirty little macro arithmetic tricks which are sadly very common in embedded systems device driver code.

 

So forgive me if I am misunderstanding ‘packed’ (and I probably am), but will this not cause the members of ‘Inner’ as a member of ‘Outer’ to become packed ((unsigned)&i == (unsigned)&c + 1), or does it simply loosen the alignment constraint within the members of ‘Outer’ to achieve what I intend?  I usually interpret ‘packed’ as all-or-nothing.  So if I replace ‘[magic attribute]’ with ‘__attribute__((packed))’, this will achieve the effect I require?  What if I want to force alignment of ‘xx’ to 2-bytes instead of 1?

 

The only reasonably portable way I know of doing this kind of thing, is to always pack structures, and to explicitly provide the dummy padding bytes (including bitfield alignment magic such as ‘int:0;’), but while these are mostly-portable, they are not absolute in the context of a specific implementation - which is where ‘__attribute__((aligned(x)))’ is necessary.

 

In any case, the incongruity of using ‘aligned’ in a ‘typedef’ versus explicitly on the member is quite unintuitive, though I fully understand the need for CLang to maintain compatibility with GCC in this regard.

 

Thanks,

 

            MartinO

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Richard Smith
Sent: 09 February 2017 18:03
To: Martin J. O'Riordan <[hidden email]>
Cc: Clang Dev <[hidden email]>; Tim Northover <[hidden email]>
Subject: Re: [cfe-dev] 'struct' member alignment query

 

The "right" way to get this effect is to use __attribute__((packed)) on the field.

 

On 9 Feb 2017 9:33 am, "Martin J. O'Riordan via cfe-dev" <[hidden email]> wrote:

Wow!  Thanks for the response, that worked perfectly.

I never would have expected that the indirect 'typedef' approach would behave any differently to the more direct annotation!  I feel that this is probably an unintended GCC behaviour/bug, though CLang is 100% right to maintain compatibility with GCC.

But it solved my problem, thanks very much,

        MartinO


-----Original Message-----
From: Tim Northover [mailto:[hidden email]]
Sent: 09 February 2017 15:08
To: Martin J. O'Riordan <[hidden email]>
Cc: cfe-dev <[hidden email]>
Subject: Re: [cfe-dev] 'struct' member alignment query

On 9 February 2017 at 02:56, Martin J. O'Riordan via cfe-dev <[hidden email]> wrote:
> Is this expected behaviour in 'clang', and if so, is there another way
> I can coerce the 'struct' member 'alignCheck2' onto another less
> constrained alignment boundary?

It's expected, but I think only because of GCC compatibility because it's really not obvious behaviour. The workaround, which still mystifies me, is to go via a typedef:

typedef AlignCheck_t __attribute__((aligned(1))) UnAlignCheck_t;

then you can put that in the struct and it'll be laid out without any padding.

Tim.

_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev

 


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: 'struct' member alignment query

Philip Reames via cfe-dev
In reply to this post by Philip Reames via cfe-dev

I can verify that this does work though, even if I don’t understand the semantics or rationale.

 

Thanks,

 

            MartinO

 

From: Martin J. O'Riordan [mailto:[hidden email]]
Sent: 09 February 2017 19:55
To: 'Richard Smith' <[hidden email]>
Cc: 'Clang Dev' <[hidden email]>; 'Tim Northover' <[hidden email]>
Subject: RE: [cfe-dev] 'struct' member alignment query

 

Hi Richard,

 

How does ‘packed’ work in this context?  I thought ‘packed’ would cause the whole aggregate to eliminate padding, rather than just changing the alignment requirements of a single member.

 

What I want is that the outer aggregate is aligned in the normal way according the maximum alignment requirements of its members.  However, I want to also have a member which itself is an aggregate with its usual internal padding retained, but changed its alignment constraint (essentially lying) within the context of the enclosing aggregate.

 

Would ‘packed’ not just unilaterally remove the padding?

 

For example (assuming char is 1-byte and int is 4, and natural alignment):

 

struct Inner {

  char c;

  int  i;

};

Inner x;

 

I can reasonably assume on a naturally aligned system that the following would hold true (undefined ISO behaviour ignored regarding address arithmetic - assume a simple conventional 32-bit Von Neumann address space):

 

sizeof(Inner) == 8

((unsigned)&x & 7) == 0

((unsigned)&x.i - (unsigned)&x.c) == 4

offsetof(Outer, c) == 0

offsetof(Outer, i) == 4

 

Now what I want to do is:

 

struct Outer {

  char aa;

  int  bb;

  char cc;

  [magic attribute] Inner xx;

  char dd;

  int  ee;

};

Outer y;

 

Such that:

 

sizeof(Outer) == 24

((unsigned)&y & 7) == 0

((unsigned)&y.xx.i - (unsigned)&y.xx.c) == 4

((unsigned)&y.xx.i - (unsigned)&y) == 13

offsetof(Inner, bb) == 4

offsetof(Inner, cc) == 8

offsetof(Inner, xx) == 9

offsetof(Inner, dd) == 17

offsetof(Inner, ee) == 20

 

so padding is still present within the layout of ‘xx’, and also as normal for the member alignment of ‘aa’, ‘bb’, ‘cc’, ‘dd’ and ‘ee’ within ‘Outer’, but not for ‘xx’.

 

This is useful when overlaying structures on registers in a memory-mapped device which can have weird arrangements, and avoid the need for dirty little macro arithmetic tricks which are sadly very common in embedded systems device driver code.

 

So forgive me if I am misunderstanding ‘packed’ (and I probably am), but will this not cause the members of ‘Inner’ as a member of ‘Outer’ to become packed ((unsigned)&i == (unsigned)&c + 1), or does it simply loosen the alignment constraint within the members of ‘Outer’ to achieve what I intend?  I usually interpret ‘packed’ as all-or-nothing.  So if I replace ‘[magic attribute]’ with ‘__attribute__((packed))’, this will achieve the effect I require?  What if I want to force alignment of ‘xx’ to 2-bytes instead of 1?

 

The only reasonably portable way I know of doing this kind of thing, is to always pack structures, and to explicitly provide the dummy padding bytes (including bitfield alignment magic such as ‘int:0;’), but while these are mostly-portable, they are not absolute in the context of a specific implementation - which is where ‘__attribute__((aligned(x)))’ is necessary.

 

In any case, the incongruity of using ‘aligned’ in a ‘typedef’ versus explicitly on the member is quite unintuitive, though I fully understand the need for CLang to maintain compatibility with GCC in this regard.

 

Thanks,

 

            MartinO

 

From: [hidden email] [[hidden email]] On Behalf Of Richard Smith
Sent: 09 February 2017 18:03
To: Martin J. O'Riordan <[hidden email]>
Cc: Clang Dev <[hidden email]>; Tim Northover <[hidden email]>
Subject: Re: [cfe-dev] 'struct' member alignment query

 

The "right" way to get this effect is to use __attribute__((packed)) on the field.

 

On 9 Feb 2017 9:33 am, "Martin J. O'Riordan via cfe-dev" <[hidden email]> wrote:

Wow!  Thanks for the response, that worked perfectly.

I never would have expected that the indirect 'typedef' approach would behave any differently to the more direct annotation!  I feel that this is probably an unintended GCC behaviour/bug, though CLang is 100% right to maintain compatibility with GCC.

But it solved my problem, thanks very much,

        MartinO


-----Original Message-----
From: Tim Northover [mailto:[hidden email]]
Sent: 09 February 2017 15:08
To: Martin J. O'Riordan <[hidden email]>
Cc: cfe-dev <[hidden email]>
Subject: Re: [cfe-dev] 'struct' member alignment query

On 9 February 2017 at 02:56, Martin J. O'Riordan via cfe-dev <[hidden email]> wrote:
> Is this expected behaviour in 'clang', and if so, is there another way
> I can coerce the 'struct' member 'alignCheck2' onto another less
> constrained alignment boundary?

It's expected, but I think only because of GCC compatibility because it's really not obvious behaviour. The workaround, which still mystifies me, is to go via a typedef:

typedef AlignCheck_t __attribute__((aligned(1))) UnAlignCheck_t;

then you can put that in the struct and it'll be laid out without any padding.

Tim.

_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev

 


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: 'struct' member alignment query

Philip Reames via cfe-dev
In reply to this post by Philip Reames via cfe-dev
On 9 Feb 2017 11:54 am, "Martin J. O'Riordan" <[hidden email]> wrote:

Hi Richard,

 

How does ‘packed’ work in this context?  I thought ‘packed’ would cause the whole aggregate to eliminate padding, rather than just changing the alignment requirements of a single member.

What packed actually does is to reduce the minimum alignment to 1. When applied to a struct, it is effectively applied to all fields.

What I want is that the outer aggregate is aligned in the normal way according the maximum alignment requirements of its members.  However, I want to also have a member which itself is an aggregate with its usual internal padding retained, but changed its alignment constraint (essentially lying) within the context of the enclosing aggregate.

 

Would ‘packed’ not just unilaterally remove the padding?

 

For example (assuming char is 1-byte and int is 4, and natural alignment):

 

struct Inner {

  char c;

  int  i;

};

Inner x;

 

I can reasonably assume on a naturally aligned system that the following would hold true (undefined ISO behaviour ignored regarding address arithmetic - assume a simple conventional 32-bit Von Neumann address space):

 

sizeof(Inner) == 8

((unsigned)&x & 7) == 0

((unsigned)&x.i - (unsigned)&x.c) == 4

offsetof(Outer, c) == 0

offsetof(Outer, i) == 4

 

Now what I want to do is:

 

struct Outer {

  char aa;

  int  bb;

  char cc;

  [magic attribute] Inner xx;

  char dd;

  int  ee;

};

Outer y;

 

Such that:

 

sizeof(Outer) == 24

((unsigned)&y & 7) == 0

((unsigned)&y.xx.i - (unsigned)&y.xx.c) == 4

((unsigned)&y.xx.i - (unsigned)&y) == 13

offsetof(Inner, bb) == 4

offsetof(Inner, cc) == 8

offsetof(Inner, xx) == 9

offsetof(Inner, dd) == 17

offsetof(Inner, ee) == 20

 

so padding is still present within the layout of ‘xx’, and also as normal for the member alignment of ‘aa’, ‘bb’, ‘cc’, ‘dd’ and ‘ee’ within ‘Outer’, but not for ‘xx’.

 

This is useful when overlaying structures on registers in a memory-mapped device which can have weird arrangements, and avoid the need for dirty little macro arithmetic tricks which are sadly very common in embedded systems device driver code.

 

So forgive me if I am misunderstanding ‘packed’ (and I probably am), but will this not cause the members of ‘Inner’ as a member of ‘Outer’ to become packed ((unsigned)&i == (unsigned)&c + 1), or does it simply loosen the alignment constraint within the members of ‘Outer’ to achieve what I intend?  I usually interpret ‘packed’ as all-or-nothing.  So if I replace ‘[magic attribute]’ with ‘__attribute__((packed))’, this will achieve the effect I require?  What if I want to force alignment of ‘xx’ to 2-bytes instead of 1?

 

The only reasonably portable way I know of doing this kind of thing, is to always pack structures, and to explicitly provide the dummy padding bytes (including bitfield alignment magic such as ‘int:0;’), but while these are mostly-portable, they are not absolute in the context of a specific implementation - which is where ‘__attribute__((aligned(x)))’ is necessary.

 

In any case, the incongruity of using ‘aligned’ in a ‘typedef’ versus explicitly on the member is quite unintuitive, though I fully understand the need for CLang to maintain compatibility with GCC in this regard.

 

Thanks,

 

            MartinO

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Richard Smith
Sent: 09 February 2017 18:03


To: Martin J. O'Riordan <[hidden email]>
Cc: Clang Dev <[hidden email]>; Tim Northover <[hidden email]>

Subject: Re: [cfe-dev] 'struct' member alignment query

 

The "right" way to get this effect is to use __attribute__((packed)) on the field.

 

On 9 Feb 2017 9:33 am, "Martin J. O'Riordan via cfe-dev" <[hidden email]> wrote:

Wow!  Thanks for the response, that worked perfectly.

I never would have expected that the indirect 'typedef' approach would behave any differently to the more direct annotation!  I feel that this is probably an unintended GCC behaviour/bug, though CLang is 100% right to maintain compatibility with GCC.

But it solved my problem, thanks very much,

        MartinO


-----Original Message-----
From: Tim Northover [mailto:[hidden email]]
Sent: 09 February 2017 15:08
To: Martin J. O'Riordan <[hidden email]>
Cc: cfe-dev <[hidden email]>
Subject: Re: [cfe-dev] 'struct' member alignment query

On 9 February 2017 at 02:56, Martin J. O'Riordan via cfe-dev <[hidden email]> wrote:
> Is this expected behaviour in 'clang', and if so, is there another way
> I can coerce the 'struct' member 'alignCheck2' onto another less
> constrained alignment boundary?

It's expected, but I think only because of GCC compatibility because it's really not obvious behaviour. The workaround, which still mystifies me, is to go via a typedef:

typedef AlignCheck_t __attribute__((aligned(1))) UnAlignCheck_t;

then you can put that in the struct and it'll be laid out without any padding.

Tim.

_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev

 



_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Loading...