[fpc-devel] Aligned array feature

J. Gareth Moreton gareth at moreton-family.com
Wed Apr 13 01:12:25 CEST 2022

In the case of a record type (which already exists), "align 16" ensures 
the whole thing is aligned on a 16-byte boundary, not the individual 
elements.  Generally it's not something to be to worried about, but in 
some special cases, such alignment is important (e.g. some of the SSE 
and AVX assembly instructions).

In regards to packed records... theoretically it just makes sure that 
there are no filler bytes in between elements even though it might cause 
less than ideal performance; e.g. a packed record with B: Byte; and I: 
Integer; fields in that order will result in the Integer field not being 
on a 4-byte boundary, sometimes requiring more complicated machine code 
to read and write to it.  Using SSE/AVX as an example again, an aligned, 
packed record would be required in the following circumstance:

type TAlignedSingleVector = packed record
   X, Y, Z, W: Single;
end align 16;

Under x86_64 at least, not specifying 'packed' will put each element on 
an 8-byte boundary rather than a 4-byte boundary (the size of Single).  
When packed together like this, a variable of this type can be passed 
into an XMM register via MOVAPS - if it's not aligned to a 16-byte 
boundary, an access violation will occur (if you can't guarantee 
alignment, you have to use MOVUPS instead, which is slower on some 
platforms), and if it's not packed, then only the first two fields get 
written into the XMM register due to the padding in between.

However, Embarcardo Delphi (which supports aligned records) is very 
unclear as to what happens if a record is both packed and aligned - 
syntactically it's allowed, but it's not clear if the resultant record 
is byte-aligned or 16-byte-aligned.  This might need testing, although 
personally I feel that 'align 16' should take precedence since it's very 
explicit and, in some implementatiosn (if not all), the record is 
aligned to the natural alignment of the first element... so with the 
above TAlignedSingleVector, it's aligned to a 4-byte boundary.  This 
goes out the window though when dealing with an array of such records 
when the size is not a multiple of the alignment.  Come to think of it, 
there's a lot of conformance testing to be done, and defining behaviour 
in more unusual circumstances, such as when you have a packed record and 
one of the elements is of an aligned type.  I'll need to start writing 
up a question sheet!

In the case of something like "type TAlignedVector = array[0..3] of 
Single align 16;", the individual elements are still packed as expected 
(I think... I know packed arrays are a thing), but the entire thing (or 
equivalently, element 0) is on a 16-byte boundary.  My reasoning for 
supporting such arrays is that it maps directly with __m128 and the 
like.  Strictly speaking, __m128 etc. are opaque types, but are 
implemented as arrays internally, although they're currently bugged in 
Free Pascal in that they're not aligned.  I submitted a fix for that 
here - https://gitlab.com/freepascal.org/fpc/source/-/merge_requests/193 
- although I found a whole other host of problems afterwards. Originally 
I started piling on the changes and fixes, but since it's still a work 
in progress, I stripped everything out except what was advertised... 
making __m128 etc. aligned.

My current question though is regarding testing.  Writing tests for 
these aligned arrays and records is simple enough, but I'm not sure what 
subdirectory/class they fall under... tbs or test/cg etc.

Gareth aka. Kit

On 12/04/2022 23:27, Karoly Balogh wrote:
> Hi,
> On Tue, 12 Apr 2022, J. Gareth Moreton via fpc-devel wrote:
>> To complement aligned records, I'm trying out an implementation of
>> aligned records.  Like how you might declare an aligned record as follows:
>> type AlignedVector = packed record
>>       X, Y: Double;
>> end align 16;
> Is this "aligned records" a new language feature already implemented, or
> it's only being proposed? What is the result of the above syntax?
> It's very confusing that it's both a "packed" and an "aligned" record.
>> Of course, that assumes such alignment support is okay in the eyes of
>> the core team (I'll need to double-check what Delphi supports so it's
>> enabled or disabled as appropriate under $MODE DELPHI).
> Well, it depends on what "align 16" would do in these cases. Only change
> the alignment between the fields/elements of the record and array? Or try
> to guarantee the alignment of the array and the record itself? The later
> might be extremely tricky to provide in a reliable and platform
> independent way, as for global variables it might depend on the behavior
> of linkers, OS loaders, and so on, and for heap variables it will depend
> on the alignment the heap allocator supports. (Although we can extend the
> heap allocator to support aligned allocations, but I'm still not a fan.)
> And for just changing the alignment inside the array/record I think it's
> too big of a syntax mess for too little benefit, which is just waiting for
> abuse, and to be a source of hard to track down bugs.
> Cheers,
> --
> Charlie

This email has been checked for viruses by Avast antivirus software.

More information about the fpc-devel mailing list