Sign in Join

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

Mailing lists / fpc-devel / 2014-09

Registers allocation technique

4 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

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

Lag Programming

I've sent an email to the list that didn't appeared so I've reformulated my partial(or complete lack) of understanding the subject. Please reply if you agree or disagree with the following two statements for x86_64 target:
1) "Unless a function calls another function, the function result register will always be in rax. Calling for inlined functions doesn't change this behaviour as it's not a real function call."
2) "Operating with rax,rbx,rcx,rdx,rbp,rsp,rsi,rdi registers should be considered faster or as fast as operating with r8,r9,r10,r11,r12,r13,r14,r15." This refers to movq, incq, cmpq...whatever.

Reply by email

Sven Barth
to Lag Programming
Lag Programming wrote
1) "Unless a function calls another function, the function result register will always be in rax. Calling for inlined functions doesn't change this behaviour as it's not a real function call."

RAX will only then be used for the function result when it is really needed (e.g. the last assignment in a function). Before that the compiler might reuse RAX as it pleases.

Please note that a function result might also be returned by memory (managed types and large records), by SSE register (floating point values) or in RAX AND RDX (records of which the size is less or equal to 16 Byte).

Regards,
Sven

Reply by email

Lag Programming
to Sven Barth

Thank you Sven.
Unfortunately I can't find a way to compress the information in few lines without losing important information. I've already done that mistake in the first mail, now I'll try to fully explain why I'm interested in the way these registers are allocated. Even if the mail is long I've tried to write it as readable as possible. I'll try to explain, step by step, with the help of 3 versions of a common function. I've chosen the "pos" function.

HERE IS THE ORIGINAL VERSION. YOU CAN SKIP THE PASCAL CODE.
{Elapsed time for this function in tests:6830ms.
# Var $result located in register r14
# Var i located in register rbx
# Var MaxLen located in register r13
# Var pc located in register r12
Temps allocated between rsp+16 and rsp+48}
Function PosORIGINAL(Const Substr : AnsiString; Const Source : AnsiString) : SizeInt;
var
  i,MaxLen : SizeInt;
  pc : pchar;
begin
  RESULT:=0;
  if Length(SubStr)>0 then
   begin
     MaxLen:=Length(source)-Length(SubStr);
     i:=0;
     ;
     while (i<=MaxLen) do
      begin
        inc(i);
        if (SubStr[1]=pc^) and
           (CompareByte(Substr[1],pc^,Length(SubStr))=0) then
         begin
           RESULT:=i;
           exit;
         end;
        inc(pc);
      end;
   end;
end;
HERE COMES THE FIRST MODIFIED VERSION OF THE ORIGINAL FUNCTION. YOU CAN SKIP THE PASCAL CODE.
{Compared with PosORIGINAL, variable "MaxLen" has been removed, the "while" loop has been replaced with a "for" loop and "if Length(SubStr)>0 then" is replaced by "if Length(SubStr)0 then".
Elapsed time for this function in tests:6000ms. That's ~800ms faster than original function.
# Var $result located in register r12
# Var pc located in register r14
# Var I located in register rbx
Temps allocated between rsp+16 and rsp+48}
{Function PosFASTER(Const Substr:AnsiString; Const Source:AnsiString):SizeInt;
var pc:pchar;
    I:SIZEINT;
begin
if Length(SubStr)0 then
   begin
   ;
   for I:=1 to (Length(source)-Length(SubStr)+1) do
       if (SubStr[1]=pc^)and(CompareByte(Substr[1],pc^,Length(SubStr))=0) then exit(I) else inc(pc);
   end;
RESULT:=0;
end;}
HERE COMES THE VERSION I'D LIKE TO RUN FASTEST. YOU CAN SKIP THE PASCAL CODE.
{Compared with function PosFASTER, variable "I:SIZEINT" has been removed and replaced with "result".
Elapsed time for this function in tests:6600ms. WHICH IS NOT WHAT I WANTED!!!
# Temps allocated between rsp+16 and rsp+40
# Var $result located in register r13
# Var pc located in register rbx}
Function PosWISHTORUNFASTEST(Const Substr:AnsiString; Const Source:AnsiString):SizeInt;
var pc:pchar;
begin
if Length(SubStr)0 then
   begin
   ;
   for RESULT:=1 to (Length(source)-Length(SubStr)+1) do
       if (SubStr[1]=pc^)and(CompareByte(Substr[1],pc^,Length(SubStr))=0) then exit else inc(pc);
   end;
RESULT:=0;
end;
   CONCLUSION
   The second version(PosFASTER) runs noticeable faster(6000ms.) compared to the original version(6800ms). I wish the third version(PosWISHTORUNFASTEST) to run even faster(less than 6000ms in my tests). Why? Because by removing the variable "I:SIZEINT;" I consider I make the job easier to the compiler. Instead of running faster, the third version(PosWISHTORUNFASTEST) runs significantly slower(6600ms) than the second version(PosFASTER)(6000ms). There are two possible reasons for this slowdown:
   1)I hit the "if...then...else" anomaly again, but I strongly doubt this is the reason of the slowdown.
   2)Using r13("result") is slower than rbx("I"). If this is the case then I'd like a solution for these situations. In my mind I have three approaches:
   a) When function result is used in a "for" loop, try to assign a "better" register.
   b) If the first approach is not good, then give the programmer the chance to act as a child: I would like one of these registers for a variable, or I don't want a register in the r12-r15 range for the function result. I think C has a directive "register" for variables, I don't know much about it.
   c) If none of the above is ok, then please explain how can I identify the functions that can use the result in "for" loops without penalties regarding speed. For example I've noticed that if a function doesn't contain a call to another function or procedure than, a int64 result is returned in rax, not r12, which should be ok for me, see "Function Pos(c : Char; Const s : AnsiString) : SizeInt;".
   I'm not much interested in "while" loops as I'm interested in "for" ones. I hope I've explained better this time.
   Thank you for patience.

Reply by email

Sergei Gorelkin
to Sven Barth: RAX will only then be used for the function result when it is really needed (e.g. the…

30.09.2014 23:34, Sven Barth пишет:

Show quoted text 7 lines
On 30.09.2014 18:37, Lag Programming wrote: > 1) "Unless a function calls another function, the function result > register will always be in rax. Calling for inlined functions doesn't > change this behaviour as it's not a real function call." RAX will only then be used for the function result when it is really needed (e.g. the last assignment in a function).

To somewhat clarify: the reqirement is to place result into RAX at the function exit point. This is unrelated to assignments to the 'result' variable in code.

Before that the compiler might reuse RAX as it pleases.

Likewise, the value of 'result' may be placed into any register or even to memory.

Regards,
Sergei

Reply by email