[fpc-devel] "Blank slate" next version of FPC

J. Gareth Moreton gareth at moreton-family.com
Sat Feb 23 01:30:00 CET 2019


 I feel like a beginner when I say that I've never used advanced records or
enumerators before, or the "for-in" loop.  There's definite room for
improvement there though, for sure.

 I think the problem with the compiler is that it seeks to keep the record
together in concurrent storage.  Storing individual fields in registers is
essentially splitting it up, even if everything stays synchronised.  Given
that the reference is an offset from %rbp, the compiler will know for sure
that the variable is local, so there won't be any problems with
multi-threading that prevents a lot of temporary register storage for
safety reasons.
 It could definitely be an area of research.  There are two possible
approaches that I can see... look via the peephole optimiser to see if a
variable on the stack can be optimised into a register, or look via the
node compiler's second pass (converts nodes into assembly language) to see
if a local variable of a record type can have its fields stored in
registers.  The latter, while preferred, may be implausible with advanced
records because of the methods.

 Gareth aka. Kit

 On Sat 23/02/19 00:17 , Benito van der Zander benito at benibela.de sent:
    Hi,

  The trick with enumerators is to never make them classes, and use
advanced records instead, I've found. This way you avoid the heap
allocation and the implicit try/finally. Also make sure you inline the
MoveNext and GetCurrent!    
  that's what I do. 

  But the generated assembly is still worse than an old for loop, because
it keeps all the fields of the record in memory. 
  for example

  >   for I in TSlice.TakeWhile(Arr, Test) do J := I; 
  generates something like this

  0000000000401290 488b45f0                 mov   
-0x10(%rbp),%rax
 0000000000401294 488b00                   mov   
(%rax),%rax
 0000000000401297 488905b22a0300           mov   
%rax,0x32ab2(%rip)        # 0x433d50 
 project1.lpr:75                           J :=
I;
 000000000040129E 488905bb2a0300           mov   
%rax,0x32abb(%rip)        # 0x433d60 
 project1.lpr:74                           for I
in TSlice.TakeWhile(Arr, Test) do
 00000000004012A5 488345f008               addq  
$0x8,-0x10(%rbp)
 project1.lpr:69                           begin
 00000000004012AA 488b45e8                 mov   
-0x18(%rbp),%rax
 project1.lpr:74                           for I
in TSlice.TakeWhile(Arr, Test) do
 00000000004012AE 483b45f0                 cmp   
-0x10(%rbp),%rax
 00000000004012B2 720a                     jb    
0x4012be 
 00000000004012B4 483b45e0                 cmp   
-0x20(%rbp),%rax
 00000000004012B8 7404                     je    
0x4012be 
 00000000004012BA b001                     mov   
$0x1,%al
 00000000004012BC eb02                     jmp   
0x4012c0 
 00000000004012BE 30c0                     xor   
%al,%al
 00000000004012C0 84c0                     test  
%al,%al
 00000000004012C2 75cc                     jne   
0x401290 

  Nearly every line is accessing some memory, when it could keep everything
in a few registers. amd64 has 16 registers, but fpc seems to only know
three when records are involved

 Cheers,
 Benito  
 Am 22.02.19 um 16:51 schrieb Ben Grasset:
      On Fri, Feb 22, 2019 at 1:07 AM Paul van Helden  wrote:
       How do you make a (for in) enumerator with a record? I don't use
them for exactly this reason, and they did seem to be another useful
language feature that turned out to be poorly implemented by Embarcadero.
(Haven't checked with FPC).    
  Here's an example (for FPC) that demonstrates it by implementing the
"take-while" pattern: 
  program TakeWhileExample; 
  {$mode Delphi}{$H+}{$J-} {$modeswitch NestedProcVars}
{$ImplicitExceptions Off} {$PointerMath On} 
  type   TSlice = record   public type     PT = ^T;     ArrayType =
array of T;   private     FFirst, FLast, FCurrent: PT;     function
GetCurrent: T; inline;   public     function GetEnumerator: TSlice;
inline;     function MoveNext: Boolean; inline;     class function
TakeWhile(const A: ArrayType; function F(const Val: T): Boolean): TSlice;
static; inline;     property Current: T read GetCurrent;   end; 
    TTestFunc = function(const Val: T): Boolean; 
    function TSlice.GetCurrent: T;   begin     Result := FCurrent^;  
end; 
    function TSlice.GetEnumerator: TSlice;   begin     Result := Self;
    with Result do FCurrent := FFirst - 1;   end; 
    function TSlice.MoveNext: Boolean;   begin     Inc(FCurrent);    
Exit((FCurrent 

Links:
------
[1] mailto:paul at planetgis.co.za
[2] mailto:fpc-devel at lists.freepascal.org
[3] http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freepascal.org/pipermail/fpc-devel/attachments/20190223/89df0680/attachment.html>


More information about the fpc-devel mailing list