[fpc-pascal]FPC 1.9.x assembler register conventions
Tomas Hajny
XHajT03 at mbox.vol.cz
Sat May 8 18:43:42 CEST 2004
From: "paradice" <lawrence at paradicesoftware.com>
To: "Pascal New" <fpc-pascal at lists.freepascal.org>
Subject: [fpc-pascal]FPC 1.9.x assembler register conventions
Send reply to: fpc-pascal at lists.freepascal.org
Date sent: Fri, 7 May 2004 17:27:36 +1200
Hi Lawrence
I'll try to answer your questions; Peter (Vreman) might correct me if
one of my answers isn't exact. ;-)
> I'm currently somewhere near the beginning of porting all my assembler
> code to the Register calling mechanism, but I have a couple of
> questions I haven't managed to find the answers to online - can anyone
> help?
>
> 1. What registers do we have to preserve when leaving the assembler
> function? (for normal functions and for class methods) From some early
> testing it seems not preserving EBX can cause problems (signalling EBX
> has changed doesn't seem to make any difference)...
The same rules apply for stdcall and regcall - EBX, ESI and EDI have
to be preserved (in addition to EBP which had to be preserved for old
FPC calling convention as well).
Regarding signalling what has/has not changed - FPC only takes this
information into account for simple assembler blocks, not for pure
assembler routines. What's worse, a possibly supplied information
about used registers is eaten without any warning (ignored silently)
for pure assembler routines. Couldn't that have been the reason of
your problem?
Example:
{$ASMMODE INTEL}
(* I'm used to Intel syntax ;-) *)
procedure ThisShouldBeOK (A: longint);
begin
asm
mov ebx, A
.
.
end ['ebx']
end;
procedure ThisIsNotOK (A: longint); assembler;
asm
mov ebx, eax
.
.
end ['ebx'];
> 2. What register can we assume values for when entering assembler
> functions? (e.g. ESI always used to be "Self" for methods but it's now
> in EAX instead.. is ESI free to use these days?)
ESI has to be preserved for both stdcall and regcall. Regarding the
values for registers, I'm quoting from an older e-mail from Peter
(not posted here previously):
----------------
Parameter assignment is done from left to right. The first 3
arguments are in registers EAX,EDX,ECX the others are on the stack.
Only ordinals (except 64bit), pointers (including var,const
parameters because those are implicit pointers), class references,
ansi/widestrings are passed in the registers. Parameters passed with
value like floats,records,arrays are passed on the stack. IOW:
Everything that fits in 32bit register is passed in the register
Example:
p(longint1,float2,longint3)
longint1: EAX
float2: Stack
longint3: EDX
Left-to-right pushing:
Framepointer and function result pointers (like shortstring,record
result) are added at the right of the parameters.
For self i still need to check Delphi. For the moment it is inserted
at the left side.
Note: We follow the Delphi convention only for i386. For other
architectures it may be different: the x86_64 will also pass int64 in
register. And for sparc,ppc,arm there is always register calling and
we follow the standard ABI.
===============================
> 3. What's the most future-proof way of using $ifdef statements to
> guard my assembler blocks? At the moment I'm using "$ifdef VER1_9" but
> this will change when 2.0 is released... Does the compiler set a
> definition for the default calling method that I can use?
It depends on what you want to achieve (which FPC versions you want
to support etc.)... There are three calling conventions which have
been used as the default in different versions of FPC till now:
oldfpccall (default in 1.0.x, but only recognized under this name in
recent 1.9.x versions), stdcall (default in older 1.1/1.9.x versions)
and regcall (default in the current 1.9.3 and future versions
including 2.0.x). If you want to assure maximum compatibility for all
FPC versions with minimum effort, modify your assembler blocks to
stdcall (i.e. make sure they preserve EBX, ESI and EDI) and add the
"stdcall" directive after declaration. This won't make them faster,
of course, but they should work in all previous and future FPC
versions. If you want to update your assembler routines to benefit
from the faster register calling convention wherever possible, you
can use the REGCALL conditional define ({$IFDEF REGCALL}); however,
I'm not sure whether this is guaranteed to exist in possible future
FPC version 3.0 as well (this kind of conditional defines might only
stay supported until the next major release). However, you need to
preserve EBX, ESI and EDI anyway (regardless whether REGCALL is
defined or not) if you want to stay compatible with older 1.1/1.9.x
versions) - this can only be omitted for {$IFDEF VER1_0}. So an
example for the maximum speed & compatibility for FPC versions 1.0.x
up to 2.0.x (at least) might look like the following:
{$ASMMODE INTEL}
(* Intel syntax is not required for you, of course *)
procedure NonsenseExample (Par1, Par2, Par3, Par4: longint);
assembler;
asm
{$IFNDEF VER1_0}
push ebx
(* let's say we will use/destroy ebx within our routine *)
{$ENDIF VER1_0}
{$IFNDEF REGCALL}
mov eax, Par1
mov edx, Par2
mov ecx, Par3
{$ENDIF REGCALL}
mov ebx, Par4
(* Par4 isn't passed in registers any more *)
{$IFNDEF VER1_0}
pop ebx
(* we need to restore ebx unless using oldfpccall *)
{$ENDIF VER1_0}
end;
Tomas
More information about the fpc-pascal
mailing list