different layout of structs for llc vs. llvm-gcc

Hi,

attached is small program in which padding inside a struct is lost when using
llc, but is not lost when using llvm-gcc or gcc. In particular, padding is
missing after codegen with llc (see (1) below, LLVM 2.7, Linux x86_64):
typedef struct {
    unsigned txnal;
    char padding[64];
    unsigned nontxnal;
} Data;
Data data;

I thought that the layout of structs was supposed to be preserved (wrong
assumption?). Otherwise, any ideas why this happens?

Thanks,
Torvald

(1)
llvm-gcc -emit-llvm -c -o padtest.bc padtest.c; llvm-ld -link-as-library
padtest.bc -o padtest2.bc; llc padtest2.bc -o padtest.s; gcc padtest.s -
lpthread -o padtestllc

0000000000400640 <updater>:
  400640: 89 3d 9a 04 20 00 mov %edi,0x20049a(%rip) #
600ae0 <data.0>
  400646: 89 3d 98 04 20 00 mov %edi,0x200498(%rip) #
600ae4 <data.2>

padtest2.bc (input to llc) still has the padding in the struct. Also note that
the padding is not removed if the addresses of data's fields are printed.

(2)
llvm-gcc padtest.c -o padtesto3 -O3 -lpthread

00000000004005d0 <updater>:
  4005d0: 89 3d ea 04 20 00 mov %edi,0x2004ea(%rip) #
600ac0 <data>
  4005d6: 89 3d 28 05 20 00 mov %edi,0x200528(%rip) #
600b04 <data+0x44>

padtest.c (947 Bytes)

It should be preserved in general; probably what's happening here is
that when you use llvm-ld, it assumes the variable data isn't used
outside of the file in question. Therefore, it ends up splitting the
global into its respective members and eliminating the apparently
unused padding. Passing -disable-internalize to llvm-ld or declaring
data as volatile should solve this.

-Eli

On Tue, Jul 13, 2010 at 10:09 AM, Torvald Riegel
> I thought that the layout of structs was supposed to be preserved (wrong
> assumption?). Otherwise, any ideas why this happens?

It should be preserved in general;

Is this a "should" or a "must"? Are there any cases in which structure layout
must be preserved besides for volatile accesses and if the data goes out to
external code?

I've seen code like the one above quite often to put data on different
cachelines, so even if it's a "should" and not a "must" it might be good to
preserve the padding. Otherwise, is there a portable way to ensure that
globals end up on a separate cacheline (without making all accesses to them
volatile)?

probably what's happening here is
that when you use llvm-ld, it assumes the variable data isn't used
outside of the file in question. Therefore, it ends up splitting the
global into its respective members and eliminating the apparently
unused padding. Passing -disable-internalize to llvm-ld or declaring
data as volatile should solve this.

-disable-internalize helps, as does making either of the two accessed fields
volatile (making the padding volatile doesn't). However, I don't want them to
be volatile. A single volatile initializing access to the first field seems to
keep the structure layout, interestingly.

Torvald

On Tue, Jul 13, 2010 at 10:09 AM, Torvald Riegel
> I thought that the layout of structs was supposed to be preserved (wrong
> assumption?). Otherwise, any ideas why this happens?

It should be preserved in general;

Is this a "should" or a "must"? Are there any cases in which structure layout
must be preserved besides for volatile accesses and if the data goes out to
external code?

LLVM generally doesn't attempt to preserve the layout of structs which
aren't externally visible or used by a volatile load/store.

I've seen code like the one above quite often to put data on different
cachelines, so even if it's a "should" and not a "must" it might be good to
preserve the padding. Otherwise, is there a portable way to ensure that
globals end up on a separate cacheline (without making all accesses to them
volatile)?

I can't think of any way of doing it without essentially trying to
trick the compiler... although there are many ways to trick LLVM.

Anyone else have ideas?

-Eli

Is alignment on a field propagated when the struct is split up?

Andrew

Oh, something I forgot earlier: the generic "don't touch this global"
directive is "__attribute__((__used__))".

-Eli

Irrelevant here; the struct in question only has 4 byte alignment.

-Eli

On Tue, Jul 13, 2010 at 10:09 AM, Torvald Riegel
> I thought that the layout of structs was supposed to be preserved (wrong
> assumption?). Otherwise, any ideas why this happens?

It should be preserved in general;

Is this a "should" or a "must"? Are there any cases in which structure layout
must be preserved besides for volatile accesses and if the data goes out to
external code?

LLVM generally doesn't attempt to preserve the layout of structs which
aren't externally visible or used by a volatile load/store.

I've seen code like the one above quite often to put data on different
cachelines, so even if it's a "should" and not a "must" it might be good to
preserve the padding. Otherwise, is there a portable way to ensure that
globals end up on a separate cacheline (without making all accesses to them
volatile)?

I can't think of any way of doing it without essentially trying to
trick the compiler... although there are many ways to trick LLVM.

Anyone else have ideas?

Is alignment on a field propagated when the struct is split up?

Irrelevant here; the struct in question only has 4 byte alignment.

Not meant to be irrelevant because the though not explained. What I
meant to say was if the padding was accomplished with an align
directive on the field, would the alignment be propagated to the
global after being split up?

Andrew

I tried this already (in the form of { uint32_t field; uint8_t pad[60];
uint32_field2;}). Alignment seems to be properly propagated, but pad is still
removed, so I believe that other globals without custom alignment constraints
could still be located in the same cachelines as field or field2.

Torvald

Good idea, thanks! Works in my example.

Torvald

I believe he was asking if adding __attribute__((aligned(NN))) to the
fields worked. It's probably a cleaner away of accomplishing
alignment than padding.

Reid

I believe he was asking if adding __attribute__((aligned(NN))) to the
fields worked. It's probably a cleaner away of accomplishing
alignment than padding.

That's what I tried (I should have put the attributes in my example below,),
And it did set up aligment properly but the individual fields do not cover the
whole cacheline (padding is removed), so other globals could be colocated on
the same line, I believe. For example, when requiring only the first
field/global to be aligned, the second field/global had been put just after the
first one in native code.

Torvald