This chapter gives detailed information on the generated code by Free Pascal. It can be useful to write external object files which will be linked to Free Pascal created code blocks.
The compiler has different register conventions, depending on the target processor used; some of the registers have specific uses during the code generation. The following section describes the generic names of the registers on a platform per platform basis. It also indicates what registers are used as scratch registers, and which can be freely used in assembler blocks.
The accumulator register is at least a 32-bit integer hardware register, and is used to return results of function calls which return integral values.
The accumulator 64-bit register is used in 32-bit environments and is defined as the group of registers which will be used when returning 64-bit integral results in function calls. This is a register pair.
This register is used for returning floating point values from functions.
The self register contains a pointer to the actual object or class. This register gives access to the data of the object or class, and the VMT pointer of that object or class.
The frame pointer register is used to access parameters in subroutines,
as well as to access local variables. References to the pushed
prameters and local variables are constructed using the frame pointer.
.
The stack pointer is used to give the address of the stack area, where the local variables and parameters to subroutines are stored.
Scratch registers are those which can be used in assembler blocks, or in external object files without requiring any saving before usage.
This indicates what registers are used for what purposes on each of the processors supported by Free Pascal. It also indicates which registers can be used as scratch registers.
Contrary to most C compilers and assemblers, all labels generated
to pascal variables and routines have mangled names. This
is done so that the compiler can do stronger type checking when parsing
the pascal code. It also permits function and procedure overloading.
The rules for mangled names for variables and typed constants are as follows:
Currently, in Free Pascal v1.0, if you declare a variable in unit name tunit, with the name _a, and you declare the same variable with name a in unit name tunit_, you will get the same mangled name. This is a limitation of the compiler which will be fixed in release v1.1.
Examples
unit testvars; interface const publictypedconst : integer = 0; var publicvar : integer; implementation const privatetypedconst : integer = 1; var privatevar : integer; end.
Will give the following assembler output under GNU as :
.file "testvars.pas" .text .data # [6] publictypedconst : integer = 0; .globl TC__TESTVARS$$_PUBLICTYPEDCONST TC__TESTVARS$$_PUBLICTYPEDCONST: .short 0 # [12] privatetypedconst : integer = 1; TC__TESTVARS$$_PRIVATETYPEDCONST: .short 1 .bss # [8] publicvar : integer; .comm U_TESTVARS_PUBLICVAR,2 # [14] privatevar : integer; .lcomm _PRIVATEVAR,2
The rules for mangled names for routines are as follows:
The following constructs
unit testman; interface type myobject = object constructor init; procedure mymethod; end; implementation constructor myobject.init; begin end; procedure myobject.mymethod; begin end; function myfunc: pointer; begin end; procedure myprocedure(var x: integer; y: longint; z : pchar); begin end; end.
will result in the following assembler file for the Intel 80x86 target:
.file "testman.pas" .text .balign 16 .globl _TESTMAN$$_$$_MYOBJECT_$$_INIT _TESTMAN$$_$$_MYOBJECT_$$_INIT: pushl %ebp movl %esp,%ebp subl $4,%esp movl $0,%edi call FPC_HELP_CONSTRUCTOR jz .L5 jmp .L7 .L5: movl 12(%ebp),%esi movl $0,%edi call FPC_HELP_FAIL .L7: movl %esi,%eax testl %esi,%esi leave ret $8 .balign 16 .globl _TESTMAN$$_$$_MYOBJECT_$$_MYMETHOD _TESTMAN$$_$$_MYOBJECT_$$_MYMETHOD: pushl %ebp movl %esp,%ebp leave ret $4 .balign 16 _TESTMAN$$_MYFUNC: pushl %ebp movl %esp,%ebp subl $4,%esp movl -4(%ebp),%eax leave ret .balign 16 _TESTMAN$$_MYPROCEDURE$INTEGER$LONGINT$PCHAR: pushl %ebp movl %esp,%ebp leave ret $12
To make the symbols externally accessible, it is possible to give nicknames to mangled names, or to change the mangled name directly. Two modifiers can be used:
The prototype for an aliased function or procedure is as follows:
Procedure AliasedProc; alias : 'AliasName';The procedure AliasedProc will also be known as AliasName. Take care, the name you specify is case sensitive (as C is).
Furthermore, the exports section of a library is also used to declare the names that will be exported by the shared library. The names in the exports section are case-sensitive (while the actual declaration of the routine is not). For more information on the creating shared libraries, chapter libraries.
Procedures and Functions are called with their parameters on the stack. Contrary to Turbo Pascal, all parameters are pushed on the stack, and they are pushed right to left, instead of left to right for Turbo Pascal. This is especially important if you have some assembly subroutines in Turbo Pascal which you would like to translate to Free Pascal.
Function results are returned in the accumulator, if they fit in the register. Methods calls (from either objects or clases) have an additional invisible parameter which is self. This parameter is the leftmost parameter within a method call (it is therefore the last parameter passed to the method).
When the procedure or function exits, it clears the stack.
Other calling methods are available for linking with external object files and libraries, these are summarized in table (CallingTable) . The first column lists the modifier you specify for a procedure declaration. The second one lists the order the paramaters are pushed on the stack. The third column specifies who is responsible for cleaning the stack: the caller or the called function. The alignment column indicates the alignment of the parameters sent to the stack area. Finally, the fifth column indicates if any registers are saved in the entry code of the subroutine.
Modifier | Pushing order | Stack cleaned by | alignment | registers saved |
<none> | Right-to-left | Function | default | None |
cdecl | Right-to-left | Caller | GCC alignment | GCC registers |
interrupt | Right-to-left | Function | default | all registers |
pascal | Left-to-right | Function | default | None |
safecall | Right-to-left | Function | default | GCC registers |
stdcall | Right-to-left | Function | GCC alignment | GCC registers |
popstack | Right-to-left | Caller | default | None |
register | Left-to-right | Caller | default | None |
More about this can be found in chapter Linking on linking. Information
on GCC registers saved, GCC stack alignment and general stack alignment
on an operating system basis can be found in Appendix . The register
modifier is currently not supported, and maps to the default calling
convention.
Furthermore, the saveregisters modifier can be used with any of the calling mechanism specifiers. When saveregisters is used, all registers will be saved on entry to the routine, and will be restored upon exit. Of course, if the routine is a function, and it normally returns its retun value in a register, that register will not be saved. Also, if the self register is used, it will also neither be saved nor restored.
When a routine is declared within the scope of a procedure or function, it is said to be nested. In this case, an additional invisible parameter is passed to the nested routine. This additional parameter is the frame pointer address of the calling routine. This permits the nested routine to access the local variables and parameters of the calling routine.
The resulting stack frame after the entry code of a simple nested procedure
has been executed is shown in table (NestedStackFrame32) .
Constructor and destructors have special invisible parameters which are passed to them. These invisible parameters are used internally to instanciate the objects and classes.
The actual invisible declaration of an object constructoir is as follows:
constructor init(_vmt : pointer; _self : pointer ...);
Where _vmt is a pointer to the virtual method table for this object. This value is nil if a constructor is called within the object instance (such as calling an ihnerited constructor).
_self is either nil if the instance must be allocated dynamically (object is declared as pointer), or the address of the object instance if the object is declared as a normal object (stored in the data area) or if the object instance has already been allocated.
The allocated instance (if allocated via new) (self) is returned in the accumulator.
The declaration of a destructor is as follows:
destructor done(_vmt : pointer; _self : pointer ...);
Where _vmt is a pointer to the virtual method table for this object. This value is nil if a destructor is called within the object instance (such as calling an ihnerited constructor), or if the object instance is a variable and not a pointer.
_self is the address of the object instance.
The actual invisible declaration of a class constructoir is as follows:
constructor init(_vmt: pointer; flag : longint; ..);
_vmt is either nil if called from the instance or if calling an inherited constructor, otherwise it points to the address of the virtual method table.
Where flag is zero if the constructor is called within the object instance or with an instance qualifier otherwise this flag is set to one.
The allocated instance (self) is returned in the accumulator.
The declaration of a destructor is as follows:
destructor done(_self : pointer; flag : longint ...);
_self is the address of the object instance.
flag is zero if the destructor is called within the object instance or with an instance qualifier otherwise this flag is set to one.
Each Pascal procedure and function begins and ends with standard epilogue and prologue code.
pushl %ebp movl %esp,%ebp
The generated exit sequence for procedure and functions looks as follows:
leave ret $xx
Where xx is the total size of the pushed parameters.
To have more information on function return values take a look at section RegConvs.
Standard entry code for procedures and functions is as follows on the 680x0 architecture:
move.l a6,-(sp) move.l sp,a6
The generated exit sequence for procedure and functions looks as follows (in the default processor mode):
unlk a6 rtd #xx
Where xx is the total size of the pushed parameters.
To have more information on function return values take a look at section RegConvs.
When a function or procedure is called, then the following is done by the compiler:
The resulting stack frame upon entering looks as in table (StackFrame) .
Each parameter passed to a routine is guaranteed to decrement the
stack pointer by a certain minimum amount. This behavior varies
from one operating system to another. For example, passing a
byte as a value parameter to a routine could either decrement the
stack pointer by 1, 2, 4 or even 8 bytes depending on the target
operating system and processor. The minimal default stack pointer decrement
value is given in Appendix .
For example, on FREEBSD, all parameters passed to a routine guarantee a minimal stack decrease of four bytes per parameter, even if the parameter actually takes less then 4 bytes to store on the stack (such as passing a byte value parameter to the stack).
Certain processors have limitations on the size of the parameters and local variables in routines. This is shown in table (CPULimits) .
Furthermore, the m68k compiler, in 68000 mode, limits the size of data elements to 32K (arrays, records, objects, etc.). This restriction does not exist in 68020 mode.