[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