View Issue Details

IDProjectCategoryView StatusLast Update
0032780FPCCompilerpublic2020-05-17 15:42
ReporterJ. Gareth Moreton Assigned ToMaciej Izak  
PrioritynormalSeverityminorReproducibilityN/A
Status assignedResolutionopen 
PlatformWin32/Win64OSWindows 7 (64-bit) 
Product Version3.1.1 
Summary0032780: [Feature Request] Per-type Byte Alignment
DescriptionOutside of global compiler directives, I would like to request a feature where you can tell the compiler that variables of a particular type must be aligned to a particular byte in memory. This is useful because aligning vectors, for example, on 16-byte boundaries allow for a significant speed boost and are actually necessary for some SSE and AVX instructions. For example:

type
  AlignedDouble = Double align 16;

  TVector4f = packed record
    X: Single;
    Y: Single;
    Z: Single;
    W: Single;
  end align 16;

  TVector4fArray = array of TVector4f align 32; { Stricter boundary, so should be okay }
Steps To ReproduceN/A
Additional InformationAlso listed here: http://wiki.lazarus.freepascal.org/Feature_Ideas#Per-type_Byte_Alignment - also contains some nuances and questions with syntax and the like.

Apparently Delphi has a similar feature, but it is not well-documented.
TagsFeature
Fixed in Revision
FPCOldBugId
FPCTarget
Attached Files

Activities

J. Gareth Moreton

2017-12-06 04:06

developer   ~0104494

Oh, this covers all platforms that support memory alignment or where it's deemed necessary, not just Windows.

Thaddy de Koning

2017-12-07 13:15

reporter   ~0104562

Last edited: 2017-12-07 13:15

View 2 revisions

Note all or most align directives are *local* directives so you can pick and choose already:
{$push} // store
{$codealign 16}
// some procedure that expects 16 byte alignment
{$pop} // restore

J. Gareth Moreton

2017-12-07 13:50

developer   ~0104563

It's not as straightforward as that unfortunately, since there are cases where a block of memory, independent to the procedure but nonetheless passed into it, has to be aligned to avoid a segmentation fault. Expecting a user of the module to use {$codealign 16} just to avoid this situation is a little much to ask for.

Thaddy de Koning

2018-01-20 09:17

reporter   ~0105950

Last edited: 2018-01-20 09:22

View 4 revisions

Well, it may be nice syntax, but as I wrote it is already there and easy to use:
Example code:
program untitled;
{$mode objfpc}
{$ALIGN 4}
type
{$PUSH}{$CODEALIGN RECORDMIN=16}
  TVector4f = packed record
    X: Single;
    Y: Single;
    Z: Single;
    W: Single;
  end; {$POP}

var
  V:TVector4f;
{$PUSH} {$CODEALIGN VARMIN=16}
  d: Double;{$POP}
  e:double;
const
{$PUSH}{$CODEALIGN CONSTMIN=32}
  c:double = 1;{$POP}
 
begin
  writeln('Record elements aligned to 16:':35,SizeOf(v) = (3*16+4)); // last entry is aligned but size is 4
  writeln('Variable aligned to 16:':35,PtrUINT(addr(d)) mod 16 = 0);
  writeln('Constant aligned to 32:':35,PtrUINT(addr(c)) mod 32 = 0);
  writeln('Default alignment is 4:':35,PtrUINT(addr(e)) mod 4 = 0);
end.

I am also not aware that Delphi can do this any easier.. Or at all with this granularity. Can you provide a link to Delphi docs?

Thaddy de Koning

2018-01-20 11:28

reporter  

aligntests.pas (867 bytes)   
program aligntests;
// demo's all possibilities from the bug report with current syntax
{$mode objfpc}
{$ALIGN 4}
type
{$PUSH}{$CODEALIGN RECORDMIN=8}
  TVector4f = packed record
    X: Single;
    Y: Single;
    Z: Single;
    W: Single;
  end; {$POP}

var
  V:TVector4f;
{$PUSH} {$CODEALIGN VARMIN=32}
  VA:ARRAY[0..1] of TVector4f;{$POP}
{$PUSH} {$CODEALIGN VARMIN=16}
  d: Double;{$POP}
  e:double;
const
{$PUSH}{$CODEALIGN CONSTMIN=32}  
  c:double = 1;{$POP}
begin 
  writeln('Record elements aligned to 8:':35,SizeOf(v) = (3*8+4));   // last entry is aligned to 8 but size is 4
  writeln('Variable aligned to 16:':35,PtrUINT(addr(d)) mod 16 = 0); 
  writeln('Constant aligned to 32:':35,PtrUINT(addr(c)) mod 32 = 0);
  writeln('Array is aligned at 32:':35,PtrUINT(addr(va)) mod 32 = 0);
  writeln('Default alignment is 4:':35,PtrUINT(addr(e)) mod 4 = 0);
end.
aligntests.pas (867 bytes)   

Marco van de Voort

2018-01-20 12:33

manager   ~0105952

Last edited: 2018-01-20 12:35

View 2 revisions

(I think if this will be implemented, it will be using attribute syntax, rather than new syntax, allowing to also add something like "vector" to signal preference for SSE and the like and other special case attributes. Extending syntax for all of them will become unwieldy. )

Thaddy de Koning

2018-01-20 15:11

reporter   ~0105954

Can you give some pseudo code, Marco?

Anyway I added a file that implements all possibilities asked here.
And you can get rid of most push/pops by careful declaration order.
I use this all the time and it works. So the syntax may not be there, but the feature most definitely is.

Maciej Izak

2018-01-20 15:37

reporter   ~0105956

@Marco, syntax with usage of "align" for records should be implemented in Delphi mode (I am aware of this for years). This feature (for Delphi mode) is in my TODO but has rather low priority.

Thaddy de Koning

2018-01-20 19:10

reporter   ~0105960

Well Maciej, the code behaves already correctly as my attachment shows?
And I am not aware of the last remaining issue: align individual elements of records to be different from the whole record.
We fixed such things with a variable record (with dummy padding) for years.

I am not against it, but we have it, although with different means.

Marco van de Voort

2018-01-21 13:30

manager   ~0105980

Maciej: is it for Delphi proper, or only the NG compiler for mobile?

Maciej Izak

2018-01-21 14:01

reporter   ~0105981

@Thady, @Marco: it works since long time (IIRC since old Delphi 6/7?). It is equivalent of {$ALIGN n} directive, AFAIK it works only for records. "align" after "end" for records in some cases is more handy than usage of {$ALIGN n}. I never tested this syntax for NG compiler.

Thaddy de Koning

2018-01-21 14:11

reporter   ~0105982

NG compilers also only accept this for records. As I expected.
Is there anything missing in my attached example? (apart from pure exotics, like changing alignment per record member).
Delphi can't do half of it.

Maciej Izak

2018-01-21 14:27

reporter   ~0105983

As alternative for proposed syntax should be possible to use "default field" (work in progress - can be tested with NewPascal) to achieve similar effect (even less modifications for compiler).

{$A16}
TAlign16<T> = record
  Value: T default;
end;

or may be implemented only Delphi compatible syntax + default field:

TAlign16<T> = record
  Value: T default;
end align 16;

TAlign32<T> = record
  Value: T default;
end align 32;

The final effect is more clean:

type
  AlignedDouble = type TAlign16<Double>;

  TVector4f = packed record
    X: Single;
    Y: Single;
    Z: Single;
    W: Single;
  end align 16;

  TVector4fArray = array of TAlign32<TVector4f>;

Marco van de Voort

2018-01-21 17:36

manager   ~0105988

If it is delphi compat, I guess there is no choice. Even if it is ugly :-)

Thaddy de Koning

2018-01-21 18:03

reporter   ~0105989

The only thing that is Delphi compatible is the syntax for record. Not the fancy "default" which I like but Delphi doesn't know about.

Anyway, maybe J. Gareth Moreton can shed some light on what's missing based on my attached example code. I can't see it. Otherwise it would be part candy and minor Delphi compatibility. Delphi can't even do what current syntax in FPC can....

J. Gareth Moreton

2018-01-21 19:49

developer   ~0105990

I dare ask... what is the attribute syntax exactly in this regard? I probably know what it is, just not the name. What are you guys proposing? I don't want to do something that people disagree with.

I would say I'm a little wary of using compiler directives like this, especially as the different align directives are easy to mix up, but if they work exactly as shown above, then the new syntax may not be necessary.

Serge Anvarov

2018-01-21 22:36

reporter   ~0105993

I figured this out as follows: for record there is already a directive {$ALIGN} for the fields inside. For other types this is not. Universal syntax is suggested.
May be better extend data alignment strategy of the compiler by {$ALIGN} for variables?

J. Gareth Moreton

2018-01-21 23:42

developer   ~0105994

Last edited: 2018-01-21 23:43

View 2 revisions

Agreed that a universal syntax is suggested, but it's a matter of deciding what exactly, given that this is a feature that is a bit of an afterthought in languages - even C++ uses implementation-specific features.

I suggested appending "align #" to the end because it's clean and easy to understand and would work with types that aren't records (e.g. type AlignedDouble = Double align 16;), and you don't risk accidentally aligning another type declaration with it. I suppose it's personal preference, but I do like to minimise the use of compiler directives.

I can't seem to find a Delphi reference - maybe I was mistaken. Either way, this could be configured to be an FPC-only feature, as awkward as that might be.

One thing of difficulty though are pointers to an aligned type, which are definitely a possibility if, for example, you wish to transform an entire array of vectors by a matrix, of which the length is initially unknown and you want to avoid the overhead of dynamic arrays for speed reasons etc. The logical thing to do would be to pass the pointer through GetMem in order to obtain the memory required, but there's no controlling the alignment of the returned memory (although I imagine it has some constraints since it seems to handle arrays of DWords without any difficulty).

Thaddy de Koning

2018-01-22 08:46

reporter   ~0106000

Last edited: 2018-01-22 08:51

View 3 revisions

" but there's no controlling the alignment of the returned memory (although I imagine it has some constraints since it seems to handle arrays of DWords without any difficulty)."
That is - unless I misunderstand - not true. In my attached example I align a typed pointer to a 8 byte aligned record to a 32 byte boundary. You don't need getmem to do that, these are compiler internals that perform the alignment.

Note that Delphi can indeed use the align xx at the close of a record. But only that.

J. Gareth Moreton

2018-01-22 16:10

developer   ~0106013

Last edited: 2018-01-22 16:10

View 2 revisions

That's useful to know that Delphi supports 'align xx' - thanks Thaddy - I guess now that means that I can make it available for all language modes, but disabling it for non-record types unless it's FPC.

The thing with GetMem is that some people use it to declare a dynamic array of some kind rather than using "array of TAlignedType" etc. I'm not sure if there's any benefit to using GetMem other than a minor speed boost and the low-level feeling of it - I'm just not sure how the allocated memory is aligned (I suspect it's 16 bytes on Win64, but that's on a specific platform).

Serge Anvarov

2018-01-22 16:35

reporter   ~0106015

There is no such syntax in delphi as 'align xx'. I think that @Thaddy had in mind what is described in the Delphi documentation [http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Internal_Data_Formats_(Delphi)]: To ensure proper alignment of the fields in an unpacked record type, the compiler inserts an unused byte before fields with an alignment of 2, and up to 3 unused bytes before fields with an alignment of 4, if required. Finally, the compiler rounds the total size of the record upward to the byte boundary specified by the largest alignment of any of the fields.

Thaddy de Koning

2018-01-22 21:44

reporter   ~0106020

There is no speed boost. The compiler simply does the align for you: the mod and the shift (basically) at compile time. Just like all my push pop magic does ;)
I am not against a syntax for that, it is just that it - the functionality - is already there and I have proven that to you through the attached code, not the text code.

J. Gareth Moreton

2018-01-31 00:22

developer   ~0106133

I didn't expect this to be assigned. I started playing around with the 'align' suffix, but after Florian had his own implementation with compiler directives, I stopped development. I would say that declaring your own vector type, for example, using a clean syntax, is more convenient than using a complex, general-purpose m128 union, but it all depends on what the higher-ups want!

Maciej Izak

2018-01-31 08:25

reporter   ~0106138

as was mentioned before - align X for records is Delphi compatible and may be added only for records in Delphi mode. This case is ofc debatable and needs more research.

J. Gareth Moreton

2018-01-31 09:23

developer   ~0106139

Oh yes, I see the post now. It would be nice in FPC mode too, but if it's Delphi only, fair enough.

J. Gareth Moreton

2019-10-21 01:07

developer   ~0118744

Has there been any progress on this front? What's the planned feature design and restrictions?

NoName

2020-05-17 15:42

reporter   ~0122878

There are some infos available:
1. https://stackoverflow.com/questions/8460862/what-does-packed-now-forces-byte-alignment-of-records-mean (message from Uwe Raabe)
2. There is also the [Align(8)] attribute which works with fields, global variables and records it seems.

Source: https://en.delphipraxis.net/topic/2824-revisiting-tthreadedqueue-and-tmonitor/?do=findComment&comment=22569

Issue History

Date Modified Username Field Change
2017-12-06 00:04 J. Gareth Moreton New Issue
2017-12-06 04:06 J. Gareth Moreton Note Added: 0104494
2017-12-07 13:15 Thaddy de Koning Note Added: 0104562
2017-12-07 13:15 Thaddy de Koning Note Edited: 0104562 View Revisions
2017-12-07 13:50 J. Gareth Moreton Note Added: 0104563
2017-12-31 15:01 J. Gareth Moreton Tag Attached: Feature
2018-01-20 09:17 Thaddy de Koning Note Added: 0105950
2018-01-20 09:18 Thaddy de Koning Note Edited: 0105950 View Revisions
2018-01-20 09:19 Thaddy de Koning Note Edited: 0105950 View Revisions
2018-01-20 09:22 Thaddy de Koning Note Edited: 0105950 View Revisions
2018-01-20 11:28 Thaddy de Koning File Added: aligntests.pas
2018-01-20 12:33 Marco van de Voort Note Added: 0105952
2018-01-20 12:35 Marco van de Voort Note Edited: 0105952 View Revisions
2018-01-20 15:11 Thaddy de Koning Note Added: 0105954
2018-01-20 15:37 Maciej Izak Note Added: 0105956
2018-01-20 19:10 Thaddy de Koning Note Added: 0105960
2018-01-21 13:30 Marco van de Voort Note Added: 0105980
2018-01-21 14:01 Maciej Izak Note Added: 0105981
2018-01-21 14:11 Thaddy de Koning Note Added: 0105982
2018-01-21 14:27 Maciej Izak Note Added: 0105983
2018-01-21 17:36 Marco van de Voort Note Added: 0105988
2018-01-21 18:03 Thaddy de Koning Note Added: 0105989
2018-01-21 19:49 J. Gareth Moreton Note Added: 0105990
2018-01-21 22:36 Serge Anvarov Note Added: 0105993
2018-01-21 23:42 J. Gareth Moreton Note Added: 0105994
2018-01-21 23:43 J. Gareth Moreton Note Edited: 0105994 View Revisions
2018-01-22 08:46 Thaddy de Koning Note Added: 0106000
2018-01-22 08:48 Thaddy de Koning Note Edited: 0106000 View Revisions
2018-01-22 08:51 Thaddy de Koning Note Edited: 0106000 View Revisions
2018-01-22 16:10 J. Gareth Moreton Note Added: 0106013
2018-01-22 16:10 J. Gareth Moreton Note Edited: 0106013 View Revisions
2018-01-22 16:35 Serge Anvarov Note Added: 0106015
2018-01-22 21:44 Thaddy de Koning Note Added: 0106020
2018-01-30 00:40 Maciej Izak Assigned To => Maciej Izak
2018-01-30 00:40 Maciej Izak Status new => assigned
2018-01-31 00:22 J. Gareth Moreton Note Added: 0106133
2018-01-31 08:25 Maciej Izak Note Added: 0106138
2018-01-31 09:23 J. Gareth Moreton Note Added: 0106139
2019-10-21 01:07 J. Gareth Moreton Note Added: 0118744
2020-05-17 15:42 NoName Note Added: 0122878