The following sections describe the general optimizations done by the compiler, they are non processor specific. Some of these require some compiler switch override while others are done automatically (those which require a switch will be noted as such).
In Free Pascal, if the operand(s) of an operator are constants, they will be evaluated at compile time.
Example
x:=1+2+3+6+5; will generate the same code as x:=17;
Furthermore, if an array index is a constant, the offset will be evaluated at compile time. This means that accessing MyData[5] is as efficient as accessing a normal variable.
Finally, calling Chr, Hi, Lo, Ord, Pred, or Succ functions with constant parameters generates no run-time library calls, instead, the values are evaluated at compile time.
Using the same constant string two or more times generates only one copy of the string constant.
Evaluation of boolean expression stops as soon as the result is known, which makes code execute faster then if all boolean operands were evaluted.
Using the in operator is always more efficient then using the
equivalent <>
, =
, <=
, >=
, <
and >
operators. This is because range comparisons can be done more easily with
in then with normal comparison operators.
Sets which contain less then 33 elements can be directly encoded using a 32-bit value, therefore no run-time library calls to evaluate operands on these sets are required; they are directly encoded by the code generator.
Assignments of constants to variables are range checked at compile time, which removes the need the generation of runtime range checking code.
Remark: This feature was not implemented before version 0.99.5 of Free Pascal.
When one of the operands in a multiplication is a power of two, they are encoded using arithmetic shifts instructions, which generates more efficient code.
Similarly, if the divisor in a div operation is a power of two, it is encoded using arithmetic shifts instructions.
The same is true when accessing array indexes which are powers of two, the address is calculated using arithmetic shifts instead of the multiply instruction.
By default all variables larger then a byte are guaranteed to be aligned at least on a word boundary.
Furthermore all pointers allocated using the standard runtime library (New and GetMem among others) are guaranteed to return pointers aligned on a quadword boundary (64-bit alignment).
Alignment of variables on the stack depends on the target processor.
Remark: Quadword alignment of pointers is not guaranteed on systems which don't use an internal heap, such as for the Win32 target.
Remark: Alignment is also done between fields in records, objects and classes, this is not the same as in Turbo Pascal and may cause problems when using disk I/O with these types. To get no alignment between fields use the packed directive or the {$PackRecords n} switch. For further information, take a look at the reference manual under the record heading.
This feature removes all unreferenced code in the final executable file, making the executable file much smaller.
Smart linking is switched on with the -Cx command-line switch, or using the {$SMARTLINK ON} global directive.
Remark: Smart linking was implemented starting with version 0.99.6 of Free Pascal.
The following runtime library routines are coded directly into the final executable : Lo, Hi, High, Sizeof, TypeOf, Length, Pred, Succ, Inc, Dec and Assigned.
Remark: Inline Inc and Dec were not completely implemented until version 0.99.6 of Free Pascal.
When using the -O1 switch, case statements in certain cases will be decoded using a jump table, which in certain cases will make the case statement execute faster.
Under certain specific conditions, the stack frame (entry and exit code for the routine, see section 3.3) will be omitted, and the variable will directly be accessed via the stack pointer.
Conditions for omission of the stack frame :
When using the -Or switch, local variables or parameters which are used very often will be moved to registers for faster access.
Remark: Register variable allocation is currently an experimental feature, and should be used with caution.
Here follows a listing of the opimizing techniques used in the compiler:
You can enable uncertain optimizations only in certain cases, otherwise you will produce a bug; the following technical description tells you when to use them:
If uncertain optimizations are enabled, the reloading optimizer assumes thatThe practical upshot of this is that you cannot use the uncertain optimizations if you access any local or global variables through pointers. In theory, this includes Var parameters, but it is all right if you don't both read the variable once through its Var reference and then read it using it's name.
- If something is written to a local/global register or a procedure/function parameter, this value doesn't overwrite the value to which a pointer points.
- If something is written to memory pointed to by a pointer variable, this value doesn't overwrite the value of a local/global variable or a procedure/function parameter.
The following example will produce bad code when you switch on uncertain optimizations:
Var temp: Longint; Procedure Foo(Var Bar: Longint); Begin If (Bar = temp) Then Begin Inc(Bar); If (Bar <> temp) then Writeln('bug!') End End; Begin Foo(Temp); End.The reason it produces bad code is because you access the global variable Temp both through its name Temp and through a pointer, in this case using the Bar variable parameter, which is nothing but a pointer to Temp in the above code.
On the other hand, you can use the uncertain optimizations if you access global/local variables or parameters through pointers, and only access them through this pointer.
For example:
Type TMyRec = Record a, b: Longint; End; PMyRec = ^TMyRec; TMyRecArray = Array [1..100000] of TMyRec; PMyRecArray = ^TMyRecArray; Var MyRecArrayPtr: PMyRecArray; MyRecPtr: PMyRec; Counter: Longint; Begin New(MyRecArrayPtr); For Counter := 1 to 100000 Do Begin MyRecPtr := @MyRecArrayPtr^[Counter]; MyRecPtr^.a := Counter; MyRecPtr^.b := Counter div 2; End; End.Will produce correct code, because the global variable MyRecArrayPtr is not accessed directly, but through a pointer (MyRecPtr in this case).
In conclusion, one could say that you can use uncertain optimizations only when you know what you're doing.
Using the -O2 switch does several optimizations in the code produced, the most notable being: