Arranging Variables

From RAD Studio
Jump to: navigation, search

Go Up to Data Types, Variables, and Constants Index


Using generics as a routine to clear the stack results in Stack variables not being laid out sequentially.

This is because a compiler does not have to lay out local variables in the order that they are declared. The following should be considered about the layout of local variables:

  • A local variable may never go on the stack if it remains in a register.
  • Several local variables share or can reuse the same stack space if they have non-overlapping live ranges.
  • Variables may be arranged based on their alignment requirements. This technique allows the compiler to optimize memory usage.

Arranging local variables

The compiler handles managed types, while users must initialize non-managed types before using them.

The example below shows a safe approach to arranging local variables. The code below shows how to use the DoSomething function to make a record with all the local variables as members.

function DoSomething(const AFileName: string = ''): Boolean; 
type
   TLocalVars = record 
      H1, H2, H3: THandle; 
      Stream: TMemoryStream; 
      hModule: System.HMODULE;
      hGlobal: THandle;
      pData: PByte;
      DataSize: Cardinal;
      LSomeRec: TSomeRec;
      LFileName: string;
   end;
var
   LLocals: TLocalVars;
begin
     FillChar(LLocals, SizeOf(LLocals), 0);
     // Here use LLocals.*
end;

Allocating locals

The logic for allocating locals is based on factors such as size and whether it’s managed or unmanaged.

Based on Size

The following example illustrates how the compiler reorders based on size. The example uses Byte and a record.

TMyRec = record 
FData: array[0..10] of Byte; 
end;

When there is only one type, or if the record follows the Byte, the order is maintained. See the example below:

//Not reordered 
var 
L1: Byte; 
L2: Byte; 
L3: Byte; 

//Not reordered 
var 
L1: Byte; 
L2: Byte; 
L3: TMyRec; 

//Not reordered
var
L1: Byte;
L2: TMyRec; 
L3: TMyRec; 

//Not reordered 
var 
L1: TMyRec; 
L2: TMyRec; 
L3: TMyRec;

Otherwise, the compiler changes the order, as shown below:

// Reordered 
var 
L1: Byte;     // Was: L1: Byte; 
L3: Byte;     // Was: L2: TMyRec; 
L2: TMyRec;   // Was: L3: Byte; 

// Reordered 
var 
L2: Byte;     // Was: L1: TMyRec; 
L3: Byte;     // Was: L2: Byte; 
L1: TMyRec;   // Was: L3: Byte; 

// Reordered 
var 
L2: Byte;     // Was: L1: TMyRec; 
L1: TMyRec;   // Was: L2: Byte; 
L3: TMyRec;   // Was: L3: TMyRec; 

// Reordered 
var 
L3: Byte;     // Was: L1: TMyRec; 
L1: TMyRec;   // Was: L2: TMyRec; 
L2: TMyRec;   // Was: L3: Byte;
Note:
Understanding that the stack grows downward, and that the example sorts based on the address of the locals, L3 is at a higher address than L2. And L2 is at a higher address than L1.

Based on Managed and non-managed

The following example demonstrates managed vs. non-managed using a string to show the pattern.

// Reordered 
var
L1: string; 
L2: Byte; 
L3: Byte; 

//Not reordered 
var 
L1: string; 
L2: Byte; 
L3: TMyRec; 

//Not reordered 
var 
L1: string; 
L2: string; 
L3: Byte; 

//Not reordered 
var 
L1: string; 
L2: string; 
L3: string; 

//Not reordered 
var 
L1: string; 
L2: string; 
L3: TMyRec; 

//Not reordered 
var 
L1: string; 
L2: TMyRec;  
L3: TMyRec;

Grouping all managed types by type simplifies array cleanup, rather than cleaning each variable separately. Additionally, grouping variables of the same size minimizes padding, thereby improving memory access efficiency.

See Also