UDT field alignment (structure packing) and gcc

General FreeBASIC programming questions.
Post Reply
St_W
Posts: 1619
Joined: Feb 11, 2009 14:24
Location: Austria
Contact:

UDT field alignment (structure packing) and gcc

Post by St_W »

I don't know whether this is already a known problem (because I remember other structure packing/aligning related problems) but the following code provides an example that causes the FreeBasic compiler to fail when using the gcc backend and compiling for a 64-bit target. The problem is that a (e.g. 12-byte) field inside a UDT is not correctly padded by gcc if that inner field was defined using the "packed" attribute obviously overriding the default alignments. In the following example the inner field is declared with "field=1" and put into an UDT with "field=8".

Code: Select all

type t12byte field=1
  as longint a
  as long b
end type

type t16byte field=8
  as t12byte	a
end type

print sizeof(t12byte)
print sizeof(t16byte)

dim dummy as t16byte
The resulting size of the UDT should be 16-bytes (4 bytes padding) but gcc does not add padding. This results in a compilation error caused by an internal size check added by the compiler:

Code: Select all

test.c:15:47: error: size of array '__$fb_structsizecheck' is negative
 #define __FB_STATIC_ASSERT( expr ) extern int __$fb_structsizecheck[(expr) ? 1 : -1]
                                               ^
test.c:20:1: note: in expansion of macro '__FB_STATIC_ASSERT'
 __FB_STATIC_ASSERT( sizeof( struct $7T16BYTE ) == 16 );
 ^
When we have a look at the generated C code we'll see that the problem could be avoided (by the compiler) by adding an explicit alignment hint for the UDT field like this:

Code: Select all

struct __attribute__((gcc_struct)) $7T12BYTE {
	int64 A __attribute__((packed, aligned(1)));
	int32 B __attribute__((packed, aligned(1)));
};
struct __attribute__((gcc_struct)) $7T16BYTE {
	struct $7T12BYTE A __attribute__((aligned(8)));
};
Of course, in this simple example it would be possible to declare the inner UDT with "field=4" instead of "field=1", which also solves the problem, but such a solution is not always available.

My questions: Is it a known problem or shall a bug report be created? Which workarounds exist? Are there any reasons for the compiler not always emitting the "alignment" attribute for each field?
St_W
Posts: 1619
Joined: Feb 11, 2009 14:24
Location: Austria
Contact:

Re: UDT field alignment (structure packing) and gcc

Post by St_W »

I tried my suggested change, but while it fixes the mentioned problem it seems to break the correct padding behaviour in other situations as the tests show. Here's my attempt, for reference:
https://github.com/swurzinger/fbc/commi ... 03db85597b

I didn't take a closer look on the new problem yet - just saw that the tests failed - but I'll definitely do. Yet, as I'm not familiar with the compiler internals at all I'd really appreciate any comments or help on/with this.

//edit: it's "structs/field-init.bas" that fails
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: UDT field alignment (structure packing) and gcc

Post by MrSwiss »

Well, I don't think it's a bug, because:
FB-Manual wrote:Field can be used to pack Types or Unions more tightly than the default layout. The most commonly used value is Field = 1, which causes the Type or Union to be packed as tightly as possible, without any padding bytes being added between the fields or at the end of the Type. Field can only be used to decrease field alignment, but it cannot be used to increase it. In order to add padding bytes, a Union with appropriate members could be used instead.
You cannot use Field to increase "padding", just to pack tighter as the compiler would have.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: UDT field alignment (structure packing) and gcc

Post by fxm »

..... Field can only be used to decrease field alignment, but it cannot be used to increase it. In order to add padding bytes, a Union with appropriate members could be used instead. .....
This description was added on 2012-10-08 by dkl.


Bug with gcc 32-bit and gcc 64-bit:

Code: Select all

type t12byte field=1
  as longint a
  as long b
end type

type t16byte
    as t12byte a
end type

print sizeof(t12byte)
print sizeof(t16byte)

dim dummy as t16byte
Compiler output:
.....\FBIDETEMP.c:15:47: error: size of array '__$fb_structsizecheck' is negative
#define __FB_STATIC_ASSERT( expr ) extern int __$fb_structsizecheck

.....\FBIDETEMP.c:20:1: note: in expansion of macro '__FB_STATIC_ASSERT'
__FB_STATIC_ASSERT( sizeof( struct $7T16BYTE ) == 16 );

Results:
Compilation failed
A workaround to the gcc bug (32-bit and 64-bit):

Code: Select all

type t12byte field=1
  as longint a
  as long b
end type

type t16byte
  union
    as t12byte a
    as longint dummy
  end union
end type

print sizeof(t12byte)
print sizeof(t16byte)

dim dummy as t16byte
12
16
Last edited by fxm on Jun 17, 2017 19:56, edited 1 time in total.
St_W
Posts: 1619
Joined: Feb 11, 2009 14:24
Location: Austria
Contact:

Re: UDT field alignment (structure packing) and gcc

Post by St_W »

MrSwiss wrote:You cannot use Field to increase "padding", just to pack tighter as the compiler would have.
Yes, sorry, I also saw that in the implementation and qword alignment is already used by default in this example so I added FIELD=8 just for clarity - it has no effect. Is there any documentation of the padding/alignment rules of fbc? //edit: sorry, found it here: http://freebasic.net/wiki/wikka.php?wak ... ructLayout I guess that the largest native datatype is used for the alignment, e.g. if a UDT contains long and byte it will be dword aligned, if it contains bytes only it will be byte aligned and if it contains a longint (as in the example) it will be qword aligned. Is this correct?
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: UDT field alignment (structure packing) and gcc

Post by MrSwiss »

St_W wrote:... contains a longint (as in the example) it will be qword aligned. Is this correct?
I think so, my tests show:
  • 1) 64bit var
    2) 32bit var
    3) = QWord alligned (since the resulting size is: 16 Byte)
Unless Field = 1 is used (12 Byte size).

The "packing" seems to take the "most convenient" member, to decide "allignment":
  • 1) a couple of U/Long's (from: BMP-header)
    2) odd number of: U/Short's
    3) = "padded" to DWord (except, Field = 1, is used)
It seems to have "preferred" sizes ... (probably: multiple of 4, if possible)

Forced smaller sizes (Field = 1), used in larger constructs (Type, Union etc.) abort GCC.
Example:

Code: Select all

Type B6_t   ' 8 byte size
    As ULong    l
    As UShort   h
End Type

Type B6f1_t Field = 1   ' 6 byte size
    As ULong    l
    As UShort   h
End Type

Type B12_t  ' don't use B6f1_t type: it'll blow GCC
    As B6_t     l
    As B6_t     h
End Type

Type B16_t
    As B12_t    l
    As ULong    h
End Type

Type B24_t
    As B12_t    l
    As B12_t    h
End Type

Dim As B12_t B12(1)

Print SizeOf(B6_t)
Print SizeOf(B6f1_t)
Print SizeOf(B12_t)
Print SizeOf(B12(0))
Print SizeOf(B16_t)
Print SizeOf(B24_t)

Sleep
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: UDT field alignment (structure packing) and gcc

Post by fxm »

St_W
Posts: 1619
Joined: Feb 11, 2009 14:24
Location: Austria
Contact:

Re: UDT field alignment (structure packing) and gcc

Post by St_W »

Thank you fxm and MrSwiss.

Meanwhile I've taken a look at the failing test(s) and found the error in my first attempt to fix the issue. It still works as described in my first post, there was just a small error in the implementation. Here's my updated patch in case anyone wants to try or comment on it:
https://github.com/swurzinger/fbc/commi ... 7d60a95b71
This one passed all the automatic tests and did also solve the problem for me. So I hope that this can be fixed in the next version of FB. I still need to write some tests for the issue. Afterwards I planned to submit a pull request and hope that the fbc devs provide further hints to make the fix ready for integration.
Post Reply