Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2003-12

default calling convention change for i386

13 messages · 6 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Peter Vreman

Hi all,
From today the default calling convention for i386 is changed from stdcall (the default since 1.9.0) to register calling. This means that you have to look at how assembler code loads the arguments and maybe store them yourself in local variables.

The register calling is compatible with delphi, so delphi assembler can now be used without changes. If there are still incompatibilities with delphi register calling please report the to the fpc-devel mailinglist including some same code.

Note that for other processors like powerpc (including the x86_64) we only support the stdcall (following the standard ABI) calling convention and all other conventions are ignored.

Peter

Reply by email

Jonas Maebe
to Peter Vreman
Peter Vreman wrote 6 lines
From today the default calling convention for i386 is changed from stdcall (the default since 1.9.0) to register calling. This means that you have to look at how assembler code loads the arguments and maybe store them yourself in local variables.

Also note that the old calling convention is still available by using the "oldfpccall" modifier (e.g. "procedure test; oldfpccall;")

Jonas

Reply by email

Ingmar Tulva
to Peter Vreman: From today the default calling convention for i386 is changed from stdcall (the default…

Bad news :(

Is this true for all {$mode }'s or only {$mode delphi} ?

On Wed, 24 Dec 2003, Peter Vreman wrote:

}Hi all,
}
}>From today the default calling convention for i386 is changed from stdcall }(the default since 1.9.0) to register calling. This means that you have to }look at how assembler code loads the arguments and maybe store them }yourself in local variables.
}
}The register calling is compatible with delphi, so delphi assembler can }now be used without changes. If there are still incompatibilities with }delphi register calling please report the to the fpc-devel mailinglist }including some same code.

------

Ingmar

------

"Experience is what you get when you don't get what you want"

Reply by email

Pavel V. Ozerski
to Ingmar Tulva

Hello Ingmar,

Wednesday, December 24, 2003, 1:21:32 PM, you wrote:

IT> Bad news :(

IT> Is this true for all {$mode }'s or only {$mode delphi} ?

Why bad, Try to add {$calling oldfpccall} into your source

Best regards, Pavel

Reply by email

Marco van de Voort
to Pavel V. Ozerski
Show quoted text 7 lines
Wednesday, December 24, 2003, 1:21:32 PM, you wrote: IT> Bad news :( IT> Is this true for all {$mode }'s or only {$mode delphi} ? Why bad, Try to add {$calling oldfpccall} into your source

To summarize it.

1.0.x has oldfpccall default
1.1.x/1.9.x has oldfpocall till about october/early november (?) 1.9.x after that had stdcall till yesterday.
1.9.x from now on will have register

IOW, you have to see for which of these the routines were designed, and then select

{$calling stdcall}
{$calling register}	// FPC 1.9.x from now on, and Delphi origin
or
{$calling oldfpccall}

Reply by email

Ingmar Tulva
to Pavel V. Ozerski: IT> Bad news :( IT> Is this true for all {$mode }'s or only {$mode delphi} ? Why bad, Try…

On Wed, 24 Dec 2003, Pavel V. Ozerski wrote:

}IT> Bad news :(
}
}IT> Is this true for all {$mode }'s or only {$mode delphi} ? }
}Why bad, Try to add {$calling oldfpccall} into your source

Just personal taste, nothing else :) No, I'm not complaining, do what you find is right.

Anyway, has someone actually analyzed how benefitial register calling convention is? Sure it provides huge speed boost in case of a function which adds two arguments together and returns the result - or is it so sure? In fact, I imagine that in most cases, register convention will eventually be detrimental to both speed and code size.

Again, no complain intended, just curiousity.

------

Ingmar

------

"Experience is what you get when you don't get what you want"

Reply by email

Anton Tichawa
to Ingmar Tulva

Hello!

Ingmar Tulva wrote 9 lines
Anyway, has someone actually analyzed how benefitial register calling convention is? Sure it provides huge speed boost in case of a function which adds two arguments together and returns the result - or is it so sure? In fact, I imagine that in most cases, register convention will eventually be detrimental to both speed and code size. Again, no complain intended, just curiousity.

I'm not a developper of free-pascal, so the following statements are just general considerations.

To me, register convention is a great step forward, concerning the economic usage of available resources.

Register convention saves opcode space in the called function, because within the instruction opcode, registers are encoded with small bit fields, whereas offsets into a stack frame are encoded as (8-, 16-, or 32-bit) words, unless the processor supports something like short offsets. Actually, I don't know if the i386 does support such short offsets. AFAIK, the 68000 does not.

Every register parameter also saves at least two memory (stack) accesses, which are considerably slower compared to register accesses and increase bus use.

When writing low-level functions in 680x0 assembler, I always pass parameters in registers (there are 15 of them, which is quite a lot). In an embedded system, I sometimes even use "global" register variables, e. g. register D7 contains, throughout the whole program, the current user ID.

A bit off-topic: I also return boolean result in a processor flag, to allow fast testing:

pascal:

begin
  if my_boolean_function then begin
    bla

assembler
jsr my_boolean_function
bcs.s yes
..
yes: bla

Implementations I know of, pass boolean result in D0, which is slower, but still faster compared to stack parameters.

In 180 degree contradiction to your opinion, I think that register convention will, in most cases, save both memory and execution time :)

As an example, consider the call

function main;
var
  a: integer;
  b: integer;
begin
  my_function(a, b);
end;

With conventional calling:

move a, -(a7) // 2 memory accesses
move b, -(a7) // 2 memory accesses
jsr my_function

WIth register calling:

move a, d0 // 1 memory access
move b, d1 // 1 memory access
jsr my_function

This example still does not include the benefits within procedure my_function, namely the saving of instruction extension words with stack offsets. In that case, 2 memory accesses (to the instruction extension word and to the stack data) are saved by register convention.

A further benefit would result from optimization between the registers passing parameters to my_function, and the registers used within function main for variables a and b, by allocating identical registers, thus removing instructions of the form "move d5, d5". That's an optimization technique easy in assembler, but difficult for a compiler, especially when maintaining register allocation through serveral levels of function calls.

If anyone does some performance testing, comparing the two conventions, please post it to the list.

Anton.

Reply by email

Anton Tichawa
to Anton Tichawa
Anton Tichawa wrote
This example still does not include the benefits within procedure my_function, namely the saving of instruction extension words with stack offsets. In that case, 2 memory accesses (to the instruction extension word and to the stack data) are saved by register convention.

Still another benefit is the reduced usage of stack memory.

Anton.

Reply by email

Peter Vreman
to Anton Tichawa: Hello! I'm not a developper of free-pascal, so the following statements are just general…
Show quoted text 15 lines
> Anyway, has someone actually analyzed how benefitial register calling > convention is? Sure it provides huge speed boost in case of a function > which adds two arguments together and returns the result - or is it so > sure? In fact, I imagine that in most cases, register convention will > eventually be detrimental to both speed and code size. > > Again, no complain intended, just curiousity. > I'm not a developper of free-pascal, so the following statements are just general considerations. To me, register convention is a great step forward, concerning the economic usage of available resources.

Most important was compatibility, second is speed. In the drystone test there was a performance gain of at least 10%.

Show quoted text 9 lines
Register convention saves opcode space in the called function, because within the instruction opcode, registers are encoded with small bit fields, whereas offsets into a stack frame are encoded as (8-, 16-, or 32-bit) words, unless the processor supports something like short offsets. Actually, I don't know if the i386 does support such short offsets. AFAIK, the 68000 does not.

Sorry, this is not correct. The code size is increased with register calling. The reason is that in the called routines the passed registers need to be saved in the local stackframe. When we have register variables support (currently not working for 1.9.x) this can be reduced.

Every register parameter also saves at least two memory (stack) accesses, which are considerably slower compared to register accesses and increase bus use.

This is true. For the Athlon the optimized parameter passing on the stack uses already mov <x>,[esp] instead of push.

Show quoted text 6 lines
When writing low-level functions in 680x0 assembler, I always pass parameters in registers (there are 15 of them, which is quite a lot). In an embedded system, I sometimes even use "global" register variables, e. g. register D7 contains, throughout the whole program, the current user ID.

For the other processors like powerpc,sparc and x86_64 the standard ABI is like register calling. Don't know for the m68k abi.

<snip>

Show quoted text 12 lines
A further benefit would result from optimization between the registers passing parameters to my_function, and the registers used within function main for variables a and b, by allocating identical registers, thus removing instructions of the form "move d5, d5". That's an optimization technique easy in assembler, but difficult for a compiler, especially when maintaining register allocation through serveral levels of function calls. If anyone does some performance testing, comparing the two conventions, please post it to the list.

At least the performance of compiler itself can't be compared easily. Because register calling increases the number of used registers so the register allocator has more work todo.

Reply by email

Ingmar Tulva
to Peter Vreman

On Wed, 24 Dec 2003, Peter Vreman wrote:

}Most important was compatibility, second is speed. In the drystone test }there was a performance gain of at least 10%.

Okay, I think this answers the question :)

}> Register convention saves opcode space in the called function, because }> within
}> the instruction opcode, registers are encoded with small bit fields, }> whereas
}> offsets into a stack frame are encoded as (8-, 16-, or 32-bit) words, }> unless
}> the processor supports something like short offsets. Actually, I don't }> know
}> if the i386 does support such short offsets. AFAIK, the 68000 does not. }
}Sorry, this is not correct. The code size is increased with register }calling. The reason is that in the called routines the passed registers }need to be saved in the local stackframe. When we have register variables }support (currently not working for 1.9.x) this can be reduced.

This is exactly what makes me doubt in the benefits of register calling. Because of the small number of registers to spare, the whole process of calling and executing a subroutine looks like this to me:

1. Calculate values of parameters and push them into stack, one by one.
2. Pop the values into appropriate registers (omitted when not using register convention)
3. Call the subroutine.
4. Push parameters into stack (omitted when not using register convention)
5. Access parameters in stack as they are needed.

Well, this is just an oversimplified theory. As Peter noted, the practice might be somewhat more pleasant :)

------

Ingmar

------

"Experience is what you get when you don't get what you want"

Reply by email

Peter Vreman
to Ingmar Tulva
Show quoted text 35 lines
On Wed, 24 Dec 2003, Peter Vreman wrote: }Most important was compatibility, second is speed. In the drystone test }there was a performance gain of at least 10%. Okay, I think this answers the question :) }> Register convention saves opcode space in the called function, because }> within }> the instruction opcode, registers are encoded with small bit fields, }> whereas }> offsets into a stack frame are encoded as (8-, 16-, or 32-bit) words, }> unless }> the processor supports something like short offsets. Actually, I don't }> know }> if the i386 does support such short offsets. AFAIK, the 68000 does not. } }Sorry, this is not correct. The code size is increased with register }calling. The reason is that in the called routines the passed registers }need to be saved in the local stackframe. When we have register variables }support (currently not working for 1.9.x) this can be reduced. This is exactly what makes me doubt in the benefits of register calling. Because of the small number of registers to spare, the whole process of calling and executing a subroutine looks like this to me: 1. Calculate values of parameters and push them into stack, one by one. 2. Pop the values into appropriate registers (omitted when not using register convention) 3. Call the subroutine. 4. Push parameters into stack (omitted when not using register convention) 5. Access parameters in stack as they are needed. Well, this is just an oversimplified theory. As Peter noted, the practice might be somewhat more pleasant :)

The push on the stack (=saving the value) is the task of the register allocator. The compiler internally uses imaginary registers. These registers will be assigned later on by the register allocator and when it has too less real registers available the register will be save temporary in the stackframe. The better the register allocator can do his job the better code is generated. In a lot of cases with simple callings like proc(i,j,k) the values of i,j and k will directly be loaded in the correct register.

For more information see the technical design (=source code) :-)

Reply by email

Pavel V. Ozerski
to Peter Vreman

Hello all,
I found a problem with dynamic arrays of shortstrings and of static arrays. These structures are not supported in Delphi but usually work normally in FPC (except this case). But if a dynamic array is declared as typized constant or a variable with initial value then trying to resize this array (using SetLength) causes the run-time error 216. Btw, if this dynamic array is declared as a usual variable (without an initial value), all works OK.
Example:

{$ifdef fpc}
{$mode delphi}
{$endif}
type
 tArr=array[0..4]of char;
var
 a:array of tArr=('00000'); //OR a:array of shorstring=('00000');
begin
 SetLength(a,2);
 a[1]:='AAAAA';
 writeln(a[1]);
end.
Best regards, Pavel

Reply by email

Pavel V. Ozerski
to Pavel V. Ozerski

Hello all,
I found also a problem with TIniFile (1.9, win32). If I try to read an information from the same .ini file using standard TIniFile methods from several instances of application at the same time, sometimes I get an error opening .INI file. I think, INI file creation flags should be supplemented with sharing-allowed constants or at least in TIniFile.Create the lines "slLines.LoadFromFile(FFileName);" and "slLines.SaveToFile(FFileName);" should be included into "Try..Except" blocks and into "Repeat..Until success" block.

Best regards, Pavel

Reply by email