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.
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) .
When the compiler encounters a call to an object's constructor, it sets up the stack frame for the call, and inserts a call to the FPC_HELP_CONSTRUCTOR procedure before issuing the call to the real constructor. The helper procedure allocates the needed memory (if needed) and inserts the VMT pointer in the object. After that, the real constructor is called.
A call to FPC_HELP_DESTRUCTOR is inserted in every destructor declaration, just before the destructor's exit sequence, it deallocates the memory allocated in the constructor.
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:
unlk a6 move.l (sp)+,a0 ; Get return address add.l #xx,sp ; Remove allocated stack move.l a0,-(sp) ; Put back return address on top of the stack
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 which can be passed to routine. This is shown in table (CPUStackLimits) .