From: "paradice" <[EMAIL PROTECTED]> To: "Pascal New" <[EMAIL PROTECTED]> Subject: [fpc-pascal]FPC 1.9.x assembler register conventions Send reply to: [EMAIL PROTECTED] 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 _______________________________________________ fpc-pascal maillist - [EMAIL PROTECTED] http://lists.freepascal.org/mailman/listinfo/fpc-pascal