Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does "Packed Now Forces Byte Alignment of Records" mean?

The What's New for Delphi XE2 contains the following.

Packed Now Forces Byte Alignment of Records

If you have legacy code that uses the packed record type and you want to link with an external DLL or with C++, you need to remove the word "packed" from your code. The packed keyword now forces byte alignment, whereas in the past it did not necessarily do this. The behavior change is related to C++ alignment compatibility changes in Delphi 2009.

I don't understand this. I'm struggling with this point: whereas in the past it did not necessarily do this. What I cannot reconcile is that packed has always resulted in byte alignment of records to the best of my knowledge. Can anyone give an example of a packed record that is not byte aligned? Obviously this would have to be in an earlier version.

Why do the docs say "if you want to link with an external DLL or with C++, you need to remove the the word packed from your code"? If the external code uses #pragma pack(1) then what are we to do if packed is off limits?

What about the $ALIGN directive? Are {$A1} and {$A-} equivalent to packed or is there some extra meaning with packed?

It seems that I'm missing something and would appreciate it if somebody could explain this. Or is the documentation just really poor?

Update

I'm reasonably convinced that the documentation is referring to alignment of the record itself rather than the layout of the record. Here's a little program that shows that the the use of packed on a record forces the alignment of the record to be 1.

program PackedRecords;
{$APPTYPE CONSOLE}
type
  TPackedRecord = packed record
    I: Int64;
  end;

  TPackedContainer = record
    B: Byte;
    R: TPackedRecord;
  end;

  TRecord = record
    I: Int64;
  end;

  TContainer = record
    B: Byte;
    R: TRecord;
  end;

var
  pc: TPackedContainer;
  c: TContainer;

begin
  Writeln(NativeInt(@pc.R)-NativeInt(@pc.B));//outputs 1
  Writeln(NativeInt(@c.R)-NativeInt(@c.B));//outputs 8
  Readln;
end.

This produces the same output on Delphi 6, 2010, XE and XE2 32 bit and XE 64 bit.

like image 406
David Heffernan Avatar asked Dec 10 '11 23:12

David Heffernan


People also ask

What does it mean to be byte aligned?

A memory address a is said to be n-byte aligned when a is a multiple of n (where n is a power of 2). In this context, a byte is the smallest unit of memory access, i.e. each memory address specifies a different byte.

What does it mean to be 16 byte aligned?

Data that's aligned on a 16 byte boundary will have a memory address that's an even number — strictly speaking, a multiple of two. Each byte is 8 bits, so to align on a 16 byte boundary, you need to align to each set of two bytes.

What is misaligned data?

Unaligned memory access is the access of data with a size of N number of bytes from an address that is not evenly divisible by the number of bytes N.

Does alignment matter memory?

Alignment matters not only for performance, but also for correctness. Some architectures will fail with an processor trap if the data is not aligned correctly, or access the wrong memory location.


3 Answers

The latest updates to the documentation have removed all of the text on which this question was based. My conclusion is that the original text was simply a documentation error.

like image 64
David Heffernan Avatar answered Oct 25 '22 01:10

David Heffernan


As I am not the Delphi compiler guy I can also mostly guess as others did: The record alignment might not be meant for aligning the members inside the record, but the record itself instead.

If you declare a record variable it is aligned to some address in memory, that is most likely aligned on a 4-byte boundary. This has been the case (as tested in D2007) for packed and unpacked records.

Now in XE2 a packed record is placed in a 1-byte boundary, while unpacked records are placed on some even boundary, which can be controlled by the align keyword. Like this:

type
  TRecAligned = record
    b1: byte;
    u1: uint64;
  end align 16;

  TRecPackedAligned = packed record
    b1: byte;
    u1: uint64;
  end align 16;

The packed record is still aligned on a 1-byte boundary while the unpacked record is aligned to a 16-byte boundary.

As I said, it is only a guess. The wording of the Embarcadero quote isn't that much clear on the suject.

like image 33
Uwe Raabe Avatar answered Oct 24 '22 23:10

Uwe Raabe


As far as I remember, record used to be packed since a version of the compiler around Delphi 5-6.

Then, for performance reasons, plain record fields were aligned, according to the settings in the project option. If you define a packed record there won't be any alignment within the record.

The text you are quoting seems related not specifically to XE2, but to a Delphi 2009 change. See this Blog entry for historical purpose.

I guess that "'Packed' Now Forces Byte Alignment of Records" refers to the Delphi 2009 {$OLDTYPELAYOUT ON} feature - which may have some implementation issue before XE2. I agree with you: it sounds like a documentation issue.

like image 1
Arnaud Bouchez Avatar answered Oct 25 '22 00:10

Arnaud Bouchez