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.
https://www.avast.com/antivirus

_______________________________________________
fpc-devel maillist  -  fpc-devel@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply via email to