Sign in Join

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

Mailing lists / fpc-devel / 2019-06

XML node dump feature

36 messages · 8 people · –

Reply by email Subscribe to this list Your subscription

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

J. Gareth Moreton

Hi everyone,

Thanks to Pierre's sponsorship and advice, the Free Pascal Compiler now contains the feature that allows you to dump the intermediate nodes generated by the compiler.  When enabled with "-dDEBUG_NODE_XML" in the compiler's source code, all units will have a "NAME-node-dump.xml" file generated alongside the compiled source.

This feature is designed to aid with debugging the compiler when introducing new features, but might prove useful in fixing existing bugs, like with https://bugs.freepascal.org/view.php?id=32913 that's been on my books for months now.

The node dumping is still a work in progress - for example, it doesn't yet dump definitions for global constants - so any extensions would be greatly appreciated.

Thank you so much Pierre for your assistance and support in this feature, and I hope it proves useful to everyone.

Gareth aka. Kit

---
This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

Reply by email

Ryan Joseph
to J. Gareth Moreton
On Jun 22, 2019, at 3:56 PM, J. Gareth Moreton wrote: Thanks to Pierre's sponsorship and advice, the Free Pascal Compiler now contains the feature that allows you to dump the intermediate nodes generated by the compiler. When enabled with "-dDEBUG_NODE_XML" in the compiler's source code, all units will have a "NAME-node-dump.xml" file generated alongside the compiled source.

Great, now you can finish pure functions. ;)

Regards,
Ryan Joseph

Reply by email

J. Gareth Moreton
to Ryan Joseph

Hah, indeed I can, and I will!

I've noticed you're particularly keen on pure functions... what's the main use that you envision for them?

Gareth

Ryan Joseph wrote 14 lines
> On Jun 22, 2019, at 3:56 PM, J. Gareth Moreton wrote: > > Thanks to Pierre's sponsorship and advice, the Free Pascal Compiler now contains the feature that allows you to dump the intermediate nodes generated by the compiler. When enabled with "-dDEBUG_NODE_XML" in the compiler's source code, all units will have a "NAME-node-dump.xml" file generated alongside the compiled source. Great, now you can finish pure functions. ;) Regards, Ryan Joseph _______________________________________________ fpc-devel maillist - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

---
This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

Reply by email

Ben Grasset
to J. Gareth Moreton
PM J. Gareth Moreton wrote
I've noticed you're particularly keen on pure functions... what's the main use that you envision for them?

I'm not the same person of course, but I'm pretty excited about them in regards to the initialization of constants.

E.G. I'd love to (eventually, at least) be able to write something roughly equivalent
to this
<https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAKxAEZSAbAQwDtRkBSAJgCFufSAZ1QBXYskwgA5NwDMeFsgYisAag6yAwnlSCCxTEwC2G7BwAMAQQuWRghcFV70IFKIIa%2BVuw6cEXILr6hiayXtZWBJhGAA7MUepaBACeMZgsxpiqACqmfsQiyASqAGqYyLLqAOzhqjmqAB6kqsnNAF6eNnVoLHqYDTHEpeWyED169XhNquPF2ap4rTOovXMLbQCUqiCNEFMbzcl7yQeqbXub1XxVACJdy6v9g6oAZsR46ejDFaqoacRMIjEfhjFYTMo/bgANgYCEEzVm30q0OIcK2HBq9zqBgIYhYSIgsMEADoGupeKpUSTpkTiclyTxKXC6c1aW0GUySZtOlY6hi7ryHn0BkM3h8WF8IZU/pgAUCALSg1ZI8kwuEIsHFKWqqnozGC7GYXHEfFSwnMsnyzmk1nM%2BlWqks1Rs1QO5ncsL3fn3WZPUXvT4qmVykgAKiV4JGqqJGuV2pRaKuWMpRrxBNpZND1ppdtUWcdSxd%2BfdGx5lj5tx9mr9rwDEqD/0BJAA9BGtVHoTGhe3IVwobqkwaU8bTSNzdTVM3s7aSfSpwWZ8T2fOS2WKwLy92a2LA0EDMZVcGm8CtBpNBA9yFVfDuyroQA3PW1OpORKaM/krgcACsfC4XDfD971JQCtE/ZpuAAs8gLpUDNHAz84NUYD2WgsDIJ/O5v00FgMM9IccTTQQ12qDdvXwiJLF9EVVCYEQiFUcsNBuJEzxeBhUEBUwMR4WhiS4ZouGJAAWZpZGJAAOcjwmo546IYxlmNYrR2M4jxZDMGohPoVRhP45oJOJWRpJsWShnk1BVHgpSmIpPgKLM2j6MslibNdVR7Jk6saIs1RsESFjyyzTzTO8uTnNUAAxALGMnDyyxsBRiiMJgFAgJ8q3opD3wo7p3CQu5cuWLK0PgswirQErTzAyKEtuKQDgYaRvykUgWGkcxWtQaR314fgnFEcQsjkWhWoIDqGoOABrEBvwATmJKooVkWQ%2BzmiTzChLg5toASmqkYTWvaqROtIbqpFawQQHMUhxpOhrSDgWAkDQWI8AYWVyEoV6Yne2UUGYNgAH19BEFgptIF53qiYgrogAAjCbSHhhQmGIZJpFG0hXqMdICAAeRYBgMfu0gsBStgPqR/ADCKPB70wK7Sf6cp6MkKQsaSzB9tOhg8HhgF0c0DB2ax/Q8CMTGHsB9h%2BH4Rh%2BauyADj%2BAgdF6aR5Xx2RLsGiQ6Ea5qjqR86GgkqF5ShYSZhl1QIFB8GtggXBCBIclZB04W3o%2BoYRq2Xq/x4MaJo2aaQFkBauF22RzFoYS5qqWhE6hCTGGkQ62pN6RLuu26Q8emBEECeiYnor7QW9/76EwfAgToUgAHcARiKXDakFrM9J86RtUBvCAQRpzct62lFYRx7YKR3g/u0PSBmvjzC4YTZET79/1oOa5q4cxZChNODuNrvs6EXO7s62f9q4Q/TvO6fz4OBnYfVkBhKAA> in
Pascal, where ultimately the structs (or records in Pascal's case) simply don't directly exist at all at runtime as they're just completely boiled down to their values.

Reply by email

J. Gareth Moreton
to Ben Grasset

"constexpr" is essentially to C++ as "pure" would be to FPC. Some of the methods that involve pointers and var parameters (which is probably the closest equivalent to the "&" parameters in C++) may be flagged as impure because they can't be dereferenced at compile time.  Nevertheless, a function that simply returns a formatted string, rather than writing directly to an output stream, is certainly doable.

There will likely be a lot of experimentation and trial and error in its development, but I'll try to at least make it consistent.

I don't quite know how to use generic advanced records yet so that specific operators are tied to them (can someone enlighten me?), but to use a vector addition operator as an example, you could probably write it akin to this:

*type*
   TVector4 = *packed **record*
     X, Y, Z, W: Single;
*end*;
*operator *+(LHS, RHS: TVector4) Result: TVector4; *vectorcall*; *pure*;
*begin*
   Result.X := LHS.X + RHS.X;
   Result.Y := LHS.Y + RHS.Y;
   Result.Z := LHS.Z + RHS.Z;
   Result.W := LHS.W + RHS.W;
*end*;

In theory, this would behave as expected, and with some additions to the compiler under x86 systems, the 4 commands can be collapsed into a single ADDPS instruction (so long as 'vectorcall' is used) that it may then be able to inline in the case of non-constant inputs (although this would have to be done at the node level). I'm not sure how well it would work on other platforms - I don't know enough about machine codes outside of the x86 family.

There is a fair bit of overlap between pure and inline routines and they may internally end up sharing the node tree that gets stored in the PPU file, although you wouldn't want to inline a particularly complex function, while a short function that deals with pointers cannot be made pure.  There are also situations where a function is pure in the sense of being a mathematical function, but are impossible to inline (e.g. the Ackermann function <https://en.wikipedia.org/wiki/Ackermann_function>).

Note I'm using the directive '*pure*' because Pascal has traditionally been a language used for programming tuition and hence has relatively intuitive keywords, so if you see '*pure*', you know it's a pure function <https://en.wikipedia.org/wiki/Pure_function> analogous to a mathematical function.  If anyone has a better directive name, go for it! (Note that because it's a directive, it won't cause code to fail to compile if you happen to have a unit or identifier named 'pure').

Gareth aka. Kit

Ben Grasset wrote 22 lines
On Sat, Jun 22, 2019 at 4:36 PM J. Gareth Moreton < > wrote: I've noticed you're particularly keen on pure functions... what's the main use that you envision for them? I'm not the same person of course, but I'm pretty excited about them in regards to the initialization of constants. E.G. I'd love to (eventually, at least) be able to write something roughly equivalent to this <https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAKxAEZSAbAQwDtRkBSAJgCFufSAZ1QBXYskwgA5NwDMeFsgYisAag6yAwnlSCCxTEwC2G7BwAMAQQuWRghcFV70IFKIIa%2BVuw6cEXILr6hiayXtZWBJhGAA7MUepaBACeMZgsxpiqACqmfsQiyASqAGqYyLLqAOzhqjmqAB6kqsnNAF6eNnVoLHqYDTHEpeWyED169XhNquPF2ap4rTOovXMLbQCUqiCNEFMbzcl7yQeqbXub1XxVACJdy6v9g6oAZsR46ejDFaqoacRMIjEfhjFYTMo/bgANgYCEEzVm30q0OIcK2HBq9zqBgIYhYSIgsMEADoGupeKpUSTpkTiclyTxKXC6c1aW0GUySZtOlY6hi7ryHn0BkM3h8WF8IZU/pgAUCALSg1ZI8kwuEIsHFKWqqnozGC7GYXHEfFSwnMsnyzmk1nM%2BlWqks1Rs1QO5ncsL3fn3WZPUXvT4qmVykgAKiV4JGqqJGuV2pRaKuWMpRrxBNpZND1ppdtUWcdSxd%2BfdGx5lj5tx9mr9rwDEqD/0BJAA9BGtVHoTGhe3IVwobqkwaU8bTSNzdTVM3s7aSfSpwWZ8T2fOS2WKwLy92a2LA0EDMZVcGm8CtBpNBA9yFVfDuyroQA3PW1OpORKaM/krgcACsfC4XDfD971JQCtE/ZpuAAs8gLpUDNHAz84NUYD2WgsDIJ/O5v00FgMM9IccTTQQ12qDdvXwiJLF9EVVCYEQiFUcsNBuJEzxeBhUEBUwMR4WhiS4ZouGJAAWZpZGJAAOcjwmo546IYxlmNYrR2M4jxZDMGohPoVRhP45oJOJWRpJsWShnk1BVHgpSmIpPgKLM2j6MslibNdVR7Jk6saIs1RsESFjyyzTzTO8uTnNUAAxALGMnDyyxsBRiiMJgFAgJ8q3opD3wo7p3CQu5cuWLK0PgswirQErTzAyKEtuKQDgYaRvykUgWGkcxWtQaR314fgnFEcQsjkWhWoIDqGoOABrEBvwATmJKooVkWQ%2BzmiTzChLg5toASmqkYTWvaqROtIbqpFawQQHMUhxpOhrSDgWAkDQWI8AYWVyEoV6Yne2UUGYNgAH19BEFgptIF53qiYgrogAAjCbSHhhQmGIZJpFG0hXqMdICAAeRYBgMfu0gsBStgPqR/ADCKPB70wK7Sf6cp6MkKQsaSzB9tOhg8HhgF0c0DB2ax/Q8CMTGHsB9h%2BH4Rh%2BauyADj%2BAgdF6aR5Xx2RLsGiQ6Ea5qjqR86GgkqF5ShYSZhl1QIFB8GtggXBCBIclZB04W3o%2BoYRq2Xq/x4MaJo2aaQFkBauF22RzFoYS5qqWhE6hCTGGkQ62pN6RLuu26Q8emBEECeiYnor7QW9/76EwfAgToUgAHcARiKXDakFrM9J86RtUBvCAQRpzct62lFYRx7YKR3g/u0PSBmvjzC4YTZET79/1oOa5q4cxZChNODuNrvs6EXO7s62f9q4Q/TvO6fz4OBnYfVkBhKAA> in Pascal, where ultimately the structs (or records in Pascal's case) simply don't directly exist at all at runtime as they're just completely boiled down to their values. _______________________________________________ fpc-devel maillist - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

---
This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

Reply by email

Ben Grasset
to J. Gareth Moreton
PM J. Gareth Moreton wrote 7 lines
"constexpr" is essentially to C++ as "pure" would be to FPC. Some of the methods that involve pointers and var parameters (which is probably the closest equivalent to the "&" parameters in C++) may be flagged as impure because they can't be dereferenced at compile time. Nevertheless, a function that simply returns a formatted string, rather than writing directly to an output stream, is certainly doable.

Well, just to be sure we're on the same page in general, the hypothetical Pascal equivalent of that program would look something like this I guess:

*program *Example;
*{$mode ObjFPC}
{$modeswitch AdvancedRecords}
**uses *SysUtils;
*type
  generic *TVec3<T> = *record
    *X, Y, Z: T;
    *class function *Create(*const *IX, IY, IZ: T): TVec3; static; pure;
    *class operator *+(constref LHS, RHS: TVec3): TVec3; *vectorcall*; pure;
    *class operator *-(constref LHS, RHS: TVec3): TVec3; *vectorcall*; pure;
    *class operator **(constref LHS, RHS: TVec3): TVec3; *vectorcall*; pure;
    *class operator */(constref LHS, RHS: TVec3): TVec3; *vectorcall*; pure;
    *// This one doesn't really need to be "pure"... normal inlining
would be fine.
    **procedure *Print; pure;
  *end*;
  *class function *TVec3.Create(*const *IX, IY, IZ: T): TVec3;
  *begin
    with *Result *do begin
      *X := IX;
      Y := IY;
      Z := IZ;
    *end*;
  *end*;
  *class operator *TVec3.+(constref LHS, RHS: TVec3): TVec3; *vectorcall*;
  *begin
    *Result := TVec3.Create(LHS.X + RHS.X, LHS.Y + RHS.Y, LHS.Z + RHS.Z);
  *end*;
  *class operator *TVec3.-(constref LHS, RHS: TVec3): TVec3; *vectorcall*;
  *begin
    *Result := TVec3.Create(LHS.X - RHS.X, LHS.Y - RHS.Y, LHS.Z - RHS.Z);
  *end*;
  *class operator *TVec3.*(constref LHS, RHS: TVec3): TVec3; *vectorcall*;
  *begin
    *Result := TVec3.Create(LHS.X * RHS.X, LHS.Y * RHS.Y, LHS.Z * RHS.Z);
  *end*;
  *class operator *TVec3./(constref LHS, RHS: TVec3): TVec3; *vectorcall*;
  *begin
    *Result := TVec3.Create(LHS.X / RHS.X, LHS.Y / RHS.Y, LHS.Z / RHS.Z);
  *end*;
  *procedure *TVec3.Print;
  *begin
    case *GetTypeKind(T) *of
      *tkFloat:
        WriteLn(Format('[%.4f, %.4f, %.4f]', [X, Y, Z]));
      tkInteger, tkInt64, tkQWord:
        WriteLn(Format('[%d, %d, %d]', [X, Y, Z]));
      *otherwise
        *WriteLn('[N/A, N/A, N/A]');
    *end*;
  *end*;
*type *TVec3F = *specialize *TVec3<Single>;
*const
  *A: TVec3F = (X: 1.2; Y: 2.4; Z: 3.8);
  B: TVec3F = (X: 2.1; Y: 4.2; Z: 8.3);
  *// You can't do the next part currently, obviously
  *C: TVec3F = A + B;
  D: TVec3F = A - B;
  E: TVec3F = A * B;
  F: TVec3F = A / B;
*begin
  *C.Print();
  D.Print();
  E.Print();
  F.Print();*end*.

Reply by email

Marģers .
to Ben Grasset

 

----- Reply to message -----
Subject: Re: [fpc-devel] XML node dump feature
Date: otrd., 25 jūn., 03:16
From: Ben Grasset
To: FPC developers' list

Show quoted text 9 lines
const A: TVec3F = (X: 1.2; Y: 2.4; Z: 3.8); B: TVec3F = (X: 2.1; Y: 4.2; Z: 8.3); // You can't do the next part currently, obviously C: TVec3F = A + B; D: TVec3F = A - B; E: TVec3F = A * B; F: TVec3F = A / B;

Sorry to say but, this should not work even with
*pure* function. Typed constants are not truly
constants.

Reply by email

Sven Barth
to Marģers .
Marģers . wrote 21 lines
----- Reply to message ----- Subject: Re: [fpc-devel] XML node dump feature Date: otrd., 25 jūn., 03:16 From: Ben Grasset To: FPC developers' list > const > A: TVec3F = (X: 1.2; Y: 2.4; Z: 3.8); > B: TVec3F = (X: 2.1; Y: 4.2; Z: 8.3); > // You can't do the next part currently, obviously > C: TVec3F = A + B; > D: TVec3F = A - B; > E: TVec3F = A * B; > F: TVec3F = A / B; > Sorry to say but, this should not work even with *pure* function. Typed constants are not truly constants.

Correct. Though considering that we're talking about *compile time* the compiler *could* treat consts (in {$J-} mode) as constants if pure functions are involved... Though it would need a bit more work as currently the compiler is "simply" piping the constant values of typed constants into the output assembly.

Regards,
Sven

Reply by email

J. Gareth Moreton
to Sven Barth

No-one said this would be an easy feature to implement.  This might be a step-by-step process, starting with simple function calls and building up to complex constant assignment. Nevertheless, at least initially, I would prefer to avoid the creation of a pure function that deals with a pointer in any way, including objects, since their values or successful creation (e.g. out of memory errors or the calling of "Fail") cannot be determined at design time.

Still, looking at your example, there's nothing to say that those record methods cannot be made pure, since the records already exist by the time they're called.  If anything, that looks like a relatively good use case to go on.

My initial complex use cases (after simple tests) will be the following:

*function *Factorial(N: QWord): QWord; *pure*; *inline*;
*var*
   X: Integer;
*begin*
   Result := 1;
*if *N < 2 *then*
     Exit;
*for *X := 2 *to *N *do*
     Result := Result * N;
*end*;

*function *RecursiveFactorial(N: QWord): QWord; *pure*; /{ Cannot be inlined on account of the recursive call, but can be made pure }/ *begin*
*if *N < 2 *then*
*begin*
    Result := 1;
    Exit;
*end*;

   Result := Result * RecursiveFactorial(N - 1);
*end*;
*function *RoughExp(X: Double): Double; *pure*;
*var*
   Count: Integer;
   WorkingValue: Double;
*begin*
/{ Calculate RoughExp(X) as e^X = 1 + X + X^2/2! + X^3/3! + ... + X^8/8! }/
   Result := Exponent;
*for *Count := 2 *to *8 *do*
*begin*
     X := X * Exponent; { Contains "X^Count" }
     Result := Result + (X / RecursiveFactorial(Count));
*end*;
end;
*function *Bad(X: QWord): QWord; *pure*;
*begin*
   Result := 0;
*while *True *do*
/{ Run forever }/;
*end*;

*const*
  Factorial6 = Factorial(6);/{ Should convert the function call into the immediate value 720 }/
  Factorial21 = Factorial(21); /{ Should return an error due to arithmetic overflow }/
  BigNumber = BadCount(0); /{ Should return an error upon realising it's in an infinite loop }/

  RFactorial20: Double = 1 / Factorial(20); /{ Typed constant }/   SqrtE = RoughExp(0.5); /{ This also tests compiler efficiency by remembering past results of RecursiveFactorial with identical inputs }/   ESquared = RoughExp(2); /{ ... as does this one if a non-recursive factorial is used, due to the previous call to RoughExp(0.5) }/

I may ask you guys for more complex examples down the line.  This is something I really want to get working!

Gareth aka. Kit

P.S. Just to clarify... we happy with the directive being '*pure*'?

Sven Barth wrote 34 lines
Am 25.06.2019 um 06:20 schrieb Marģers . via fpc-devel: > > ----- Reply to message ----- > Subject: Re: [fpc-devel] XML node dump feature > Date: otrd., 25 jūn., 03:16 > From:  Ben Grasset > To:  FPC developers' list > > >> const >>    A: TVec3F = (X: 1.2; Y: 2.4; Z: 3.8); >>    B: TVec3F = (X: 2.1; Y: 4.2; Z: 8.3); >>    // You can't do the next part currently, obviously >>    C: TVec3F = A + B; >>    D: TVec3F = A - B; >>    E: TVec3F = A * B; >>    F: TVec3F = A / B; >> > Sorry to say but, this should not work even with > *pure* function.  Typed constants are not truly > constants. Correct. Though considering that we're talking about *compile time* the compiler *could* treat consts (in {$J-} mode) as constants if pure functions are involved... Though it would need a bit more work as currently the compiler is "simply" piping the constant values of typed constants into the output assembly. Regards, Sven _______________________________________________ fpc-devel maillist  -  https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

---
This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

Reply by email

J. Gareth Moreton
to J. Gareth Moreton
Sorry for the mistake in "RoughExp" - the first line is supposed to be
"Result := 1 + X;", not "Result := Exponent;".
J. Gareth Moreton wrote 131 lines
No-one said this would be an easy feature to implement.  This might be a step-by-step process, starting with simple function calls and building up to complex constant assignment. Nevertheless, at least initially, I would prefer to avoid the creation of a pure function that deals with a pointer in any way, including objects, since their values or successful creation (e.g. out of memory errors or the calling of "Fail") cannot be determined at design time. Still, looking at your example, there's nothing to say that those record methods cannot be made pure, since the records already exist by the time they're called.  If anything, that looks like a relatively good use case to go on. My initial complex use cases (after simple tests) will be the following: *function *Factorial(N: QWord): QWord; *pure*; *inline*; *var*   X: Integer; *begin*   Result := 1; *if *N < 2 *then*     Exit; *for *X := 2 *to *N *do*     Result := Result * N; *end*; *function *RecursiveFactorial(N: QWord): QWord; *pure*; /{ Cannot be inlined on account of the recursive call, but can be made pure }/ *begin* *if *N < 2 *then* *begin*     Result := 1;     Exit; *end*;   Result := Result * RecursiveFactorial(N - 1); *end*; *function *RoughExp(X: Double): Double; *pure*; *var*   Count: Integer;   WorkingValue: Double; *begin* /{ Calculate RoughExp(X) as e^X = 1 + X + X^2/2! + X^3/3! + ... + X^8/8! }/   Result := Exponent; *for *Count := 2 *to *8 *do* *begin*     X := X * Exponent; { Contains "X^Count" }     Result := Result + (X / RecursiveFactorial(Count)); *end*; end; *function *Bad(X: QWord): QWord; *pure*; *begin*   Result := 0; *while *True *do* /{ Run forever }/; *end*; *const*   Factorial6 = Factorial(6);/{ Should convert the function call into the immediate value 720 }/   Factorial21 = Factorial(21); /{ Should return an error due to arithmetic overflow }/   BigNumber = BadCount(0); /{ Should return an error upon realising it's in an infinite loop }/   RFactorial20: Double = 1 / Factorial(20); /{ Typed constant }/   SqrtE = RoughExp(0.5); /{ This also tests compiler efficiency by remembering past results of RecursiveFactorial with identical inputs }/   ESquared = RoughExp(2); /{ ... as does this one if a non-recursive factorial is used, due to the previous call to RoughExp(0.5) }/ I may ask you guys for more complex examples down the line. This is something I really want to get working! Gareth aka. Kit P.S. Just to clarify... we happy with the directive being '*pure*'? On 25/06/2019 06:20, Sven Barth via fpc-devel wrote: > Am 25.06.2019 um 06:20 schrieb Marģers . via fpc-devel: >> >> ----- Reply to message ----- >> Subject: Re: [fpc-devel] XML node dump feature >> Date: otrd., 25 jūn., 03:16 >> From:  Ben Grasset >> To:  FPC developers' list >> >> >>> const >>>    A: TVec3F = (X: 1.2; Y: 2.4; Z: 3.8); >>>    B: TVec3F = (X: 2.1; Y: 4.2; Z: 8.3); >>>    // You can't do the next part currently, obviously >>>    C: TVec3F = A + B; >>>    D: TVec3F = A - B; >>>    E: TVec3F = A * B; >>>    F: TVec3F = A / B; >>> >> Sorry to say but, this should not work even with >> *pure* function.  Typed constants are not truly >> constants. > Correct. Though considering that we're talking about *compile time* > the compiler *could* treat consts (in {$J-} mode) as constants if > pure functions are involved... Though it would need a bit more work > as currently the compiler is "simply" piping the constant values of > typed constants into the output assembly. > > Regards, > Sven > _______________________________________________ > fpc-devel maillist  - > https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel > > <https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient> Virus-free. www.avast.com <https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient> <#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2> _______________________________________________ fpc-devel maillist - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

---
This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

Reply by email

J. Gareth Moreton
to denisgolovan

Honestly, I consider optimisation to be my speciality, especially given a lot of my contributions to FPC were peephole optimisations.

I did think about optimisations such as "for X := 0 to SomeFunction(Para) do", where SomeFunction is a pure function, but then I was told that the upper limit is only computed once at the very beginning and doesn't change with subsequent iterations. Still, if an optimisation reveals itself, I'll be more than willing to develop it.

Can I have an example of "hoisting pure functions out of loops" that you speak of?

Given how big this undertaking is, I will be writing a design specification as I go so my changes are a little more apparent and so you guys can rip it to shreds if I make a mistake.

Gareth aka. Kit

denisgolovan wrote 5 lines
Hi Gareth I'd like to know if the scope of your interest in "pure" is just initialization of constants or you plan to implement some optimizations as well? I mean something like hoisting pure functions out loops.

---
This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

Reply by email

denisgolovan
to J. Gareth Moreton
Can I have an example of "hoisting pure functions out of loops" that you speak of?

Sure.
I mean something like following (completely artificial example):

function PureFunc1: ...
function F(arg: Integer): Integer;
var s:Integer;
begin
  s:=0;
  for i:=0 to 100 do
     s:=s+PureFunc(arg);
  Result:=s;
end;

Of course, the real value of pure functions for optimization oppotunities is some combination of generics and closures, but that's completely another story...

Regards, Denis Golovan

Reply by email

J. Gareth Moreton
to denisgolovan

Initially, F probably wouldn't be optimised because 'arg' is not constant.  However, if F were marked as inline (not pure) and its actual parameter is a constant (say, 5, for the sake of argument), then there is definitely potential for optimisation, since it would be expanded to the following:

s := 0;
for i := 0 to 100 do
   s := s + PureFunc(5);
Result := s;

In this case, PureFunc(5) would be replaced with its pre-calculated result, and some node analysis could potentially replace the entire for-loop with s := s + x, where x is equal to 101 * PureFunc(5), since it's now dealing with simple numbers.

Now for the case where F is not pure or inline, it may take a bit more thought in allowing the compiler to detect the mathematical equivalence and replace the for-loop with s := s + 101 * PureFunc(arg), but it's certainly doable.  Going back to the original subject title, this is one reason why I pushed to get the XML node dump feature implemented, because it provides valuable research data for the development of such optimisations.  Such an optimisation may have to be -O4 though because of a side-effect with range check errors - if the error is handled, s might contain a different value to what is expected (e.g. if s was initially $7FFFFFF0 and PureFunc(arg) = 1, then assuming s isn't written to before the error is raised, it will still contain $7FFFFFF0 rather than $7FFFFFFF that would occur if the for-loop ran a few times).

Long story short, it took me a brief moment to see how the optimisation would work until I thought about it a bit more, but if PureFunc is indeed pure, then that could certainly be a development goal.

Thank you very much for that!

Gareth aka. Kit

denisgolovan wrote 17 lines
> Can I have an example of "hoisting pure functions out of loops" that you > speak of? Sure. I mean something like following (completely artificial example): function PureFunc1: ... function F(arg: Integer): Integer; var s:Integer; begin s:=0; for i:=0 to 100 do s:=s+PureFunc(arg); Result:=s; end; Of course, the real value of pure functions for optimization oppotunities is some combination of generics and closures, but that's completely another story...

---
This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

Reply by email

denisgolovan
to J. Gareth Moreton
Long story short, it took me a brief moment to see how the optimisation would work until I thought about it a bit more, but if PureFunc is indeed pure, then that could certainly be a development goal.

Yes, you've got the idea.
The complete system has a lot of issues and edge cases.

However, my point is that "inline" and "pure" should be completely orthogonal functionalities and be mixed Regards,
Denis Golovan

Reply by email

J. Gareth Moreton
to Ryan Joseph

Admittedly I only just set one up, admittedly to make projects to show off to prospective employers - I haven't got anything to show yet though.

Would you suggest I share patch files on there?

Gareth aka. Kit

Ryan Joseph wrote 14 lines
> On Jun 25, 2019, at 4:11 AM, J. Gareth Moreton wrote: > > Given how big this undertaking is, I will be writing a design specification as I go so my changes are a little more apparent and so you guys can rip it to shreds if I make a mistake. Do you have a public GitHub you publish to? Just curious to see your work on this... Regards, Ryan Joseph _______________________________________________ fpc-devel maillist - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

---
This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

Reply by email

Ben Grasset
to Sven Barth: Correct. Though considering that we're talking about *compile time* the compiler *could*…

On Tue, Jun 25, 2019 at 1:20 AM Sven Barth via fpc-devel <

Show quoted text 6 lines
wrote: Correct. Though considering that we're talking about *compile time* the compiler *could* treat consts (in {$J-} mode) as constants if pure functions are involved...

I'd be fine with the {$J-} requirement (I tend to use {$J-} most of the time anyways, actually.)

Again, I'm aware all of this will take a while to implement, and will be an incremental process. Overall though I think proper compile-time constant initialization needs to be something `pure` eventually enables, as if it doesn't then it just becomes "slightly-different-inlining". (And like denisgolovan pointed out in another message, inlining should remain a separate concept that can be mixed with pure, or not mixed with it, depending on the use case. E.G. a C++ compiler will automatically inline basically anything at -O3, but it will not let you use a non-constexpr function to initialize an actual constexpr value.)

Reply by email

J. Gareth Moreton
to Ben Grasset

While pure and inline do have a lot of overlap and, internally, probably share some features for convenience (like a copy of the node tree), they are not the same - for example, recursive functions can be pure, but not inline, and functions that dereference pointers can be inlined, but not made pure.  The constant initialisation is one reason for pure functions, as it might be more convenient to declare things like "const ONE_OVER_E = Exp(-1);" instead of "const ONE_OVER_E = 0.36787944117144232159552377016146;" (although Exp might be an intrinsic rather than a function - not sure).

Note that while there's room for auto-inlining certain routines, I would not recommend "auto-puring" because determining if a function actually is pure or not is likely going to be an expensive operation, and it's more likely that a function will not be pure.

Gareth aka. Kit

Ben Grasset wrote 27 lines
On Tue, Jun 25, 2019 at 1:20 AM Sven Barth via fpc-devel < > wrote: Correct. Though considering that we're talking about *compile time* the compiler *could* treat consts (in {$J-} mode) as constants if pure functions are involved... I'd be fine with the {$J-} requirement (I tend to use {$J-} most of the time anyways, actually.) Again, I'm aware all of this will take a while to implement, and will be an incremental process. Overall though I think proper compile-time constant initialization needs to be something `pure` eventually enables, as if it doesn't then it just becomes "slightly-different-inlining". (And like denisgolovan pointed out in another message, inlining should remain a separate concept that can be mixed with pure, or not mixed with it, depending on the use case. E.G. a C++ compiler will automatically inline basically anything at -O3, but it will not let you use a non-constexpr function to initialize an actual constexpr value.) _______________________________________________ fpc-devel maillist - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

---
This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

Reply by email

Ryan Joseph
to J. Gareth Moreton: Admittedly I only just set one up, admittedly to make projects to show off to prospective…
Show quoted text 5 lines
On Jun 25, 2019, at 11:12 AM, J. Gareth Moreton wrote: Admittedly I only just set one up, admittedly to make projects to show off to prospective employers - I haven't got anything to show yet though. Would you suggest I share patch files on there?

I always upload my commits to GitHub for backups/records which is nice because it’s public also so you can spy on me :), for example:

https://github.com/genericptr/freepascal/tree/array_prop_overload

If you use git anyways and have a client app (I’m using Sublime Merge currently) it’s pretty easy to just press a button and push changes to the server.

Regards,
Ryan Joseph

Reply by email

Ben Grasset
to J. Gareth Moreton: While pure and inline do have a lot of overlap and, internally, probably share some…

On Tue, Jun 25, 2019 at 11:58 AM J. Gareth Moreton <

Show quoted text 6 lines
wrote: While pure and inline do have a lot of overlap and, internally, probably share some features for convenience (like a copy of the node tree), they are not the same

Oh yeah, not arguing I just meant they'd be, practically speaking, much *closer* to the same from the viewpoint of an average programmer without `pure` having constant initialization capabilities.

On Tue, Jun 25, 2019 at 11:58 AM J. Gareth Moreton <

wrote: recursive functions can be pure, but not inline

That's actually not true! Try it yourself. IIRC FPC will happily inline recursive functions into themselves by at least one full "pass".

Reply by email

J. Gareth Moreton
to Ryan Joseph: I always upload my commits to GitHub for backups/records which is nice because it’s…

This is my repository.  Only one private test project so far that a friend and I are using to learn Test Driven Development and other languages.

https://github.com/CuriousKit

Currently I only use git locally, bit given how big this task will be and also how iterative it will be, I will likely use GitHub in this case.

Gareth aka. Kit

Ryan Joseph wrote 20 lines
> On Jun 25, 2019, at 11:12 AM, J. Gareth Moreton wrote: > > Admittedly I only just set one up, admittedly to make projects to show off to prospective employers - I haven't got anything to show yet though. > > Would you suggest I share patch files on there? I always upload my commits to GitHub for backups/records which is nice because it’s public also so you can spy on me :), for example: https://github.com/genericptr/freepascal/tree/array_prop_overload If you use git anyways and have a client app (I’m using Sublime Merge currently) it’s pretty easy to just press a button and push changes to the server. Regards, Ryan Joseph _______________________________________________ fpc-devel maillist - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

---
This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

Reply by email

Ryan Joseph
to J. Gareth Moreton
Show quoted text 7 lines
On Jun 25, 2019, at 1:40 PM, J. Gareth Moreton wrote: This is my repository. Only one private test project so far that a friend and I are using to learn Test Driven Development and other languages. https://github.com/CuriousKit Currently I only use git locally, bit given how big this task will be and also how iterative it will be, I will likely use GitHub in this case.

Great, put up what you have if you some time. I’d be curious to see how this works.

Regards,
Ryan Joseph

Reply by email

J. Gareth Moreton
to Ryan Joseph

At the momentI don't really have anything, but I will upload something soon.  I've only really been experimenting so far, but first I figure I'll need to get the node tree generated if "pure" is specified as well as "inline".

Gareth aka. Kit

Ryan Joseph wrote 18 lines
> On Jun 25, 2019, at 1:40 PM, J. Gareth Moreton wrote: > > This is my repository. Only one private test project so far that a friend and I are using to learn Test Driven Development and other languages. > > https://github.com/CuriousKit > > Currently I only use git locally, bit given how big this task will be and also how iterative it will be, I will likely use GitHub in this case. Great, put up what you have if you some time. I’d be curious to see how this works. Regards, Ryan Joseph _______________________________________________ fpc-devel maillist - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

---
This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

Reply by email

Jonas Maebe
to denisgolovan: Sure. I mean something like following (completely artificial example): function…
denisgolovan wrote 15 lines
> Can I have an example of "hoisting pure functions out of loops" that you > speak of? Sure. I mean something like following (completely artificial example): function PureFunc1: ... function F(arg: Integer): Integer; var s:Integer; begin s:=0; for i:=0 to 100 do s:=s+PureFunc(arg); Result:=s; end;

You don't need any kind of new source code constructs for optimizing that. The LLVM backend can already do that; in the worst case, only when LTO is enabled (in case the function is declared in another unit)

Jonas

Reply by email

denisgolovan
to Jonas Maebe
Show quoted text 5 lines
You don't need any kind of new source code constructs for optimizing that. The LLVM backend can already do that; in the worst case, only when LTO is enabled (in case the function is declared in another unit) Jonas

I am pretty sure LLVM is advanced enough to make that analysis itself for primitive types. But what about something more interesting - arrays, matrices, trees, hash tables, etc?

Regards, Denis Golovan

Reply by email

Jonas Maebe
to denisgolovan
denisgolovan wrote 6 lines
> You don't need any kind of new source code constructs for optimizing > that. The LLVM backend can already do that; in the worst case, only when > LTO is enabled (in case the function is declared in another unit) I am pretty sure LLVM is advanced enough to make that analysis itself for primitive types. But what about something more interesting - arrays, matrices, trees, hash tables, etc?

Complex types are built from primitive types. And if FPC can verify the "pure" attribute is valid, then LLVM can probably also come up with it. In fact, if FPC can verify it is valid, it can also come up with it itself and add it as an annotation in the LLVM bitcode.

Jonas

Reply by email

denisgolovan
to Jonas Maebe
Complex types are built from primitive types. And if FPC can verify the "pure" attribute is valid, then LLVM can probably also come up with it. In fact, if FPC can verify it is valid, it can also come up with it itself and add it as an annotation in the LLVM bitcode.

AFAIU, "pure" attribute is planned to be user's responsibility and that's probably ok.

e.g. what about returning dynamic arrays? Like:

type TIntArray = array of Integer;
function ArrInit(): TIntArray;
begin
  SetLength(Result, 1);
  Result[0]:=1;
end;
... I am afraid from LLVM perspective it wouldn't be "pure" function (memory allocation, initialization, etc.).
From Pascal user's perspective, I would consider it pure.

Second, LLVM is just one backend, right?

Regards, Denis Golovan

Reply by email

Jonas Maebe
to denisgolovan
denisgolovan wrote 6 lines
> Complex types are built from primitive types. And if FPC can verify the > "pure" attribute is valid, then LLVM can probably also come up with it. > In fact, if FPC can verify it is valid, it can also come up with it > itself and add it as an annotation in the LLVM bitcode. AFAIU, "pure" attribute is planned to be user's responsibility and that's probably ok.

That would definitely not be okay.

Jonas

Reply by email

Ben Grasset
to denisgolovan: AFAIU, "pure" attribute is planned to be user's responsibility and that's probably ok.…
PM denisgolovan wrote 9 lines
e.g. what about returning dynamic arrays? Like: type TIntArray = array of Integer; function ArrInit(): TIntArray; begin SetLength(Result, 1); Result[0]:=1; end;

Stuff like that is pretty much impossible, because you can't dynamically allocate memory at compile time.

See here
<https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(j:1,lang:c%2B%2B,source:'%23include+%3Ciostream%3E%0A%23include+%3Cmemory%3E%0A%23include+%3Cvector%3E%0A%23include+%3Cstring%3E%0A%0Ausing+std::cout%3B%0Ausing+std::unique_ptr%3B%0Ausing+std::make_unique%3B%0Ausing+std::vector%3B%0Ausing+std::string%3B%0A%0Atemplate+%3Ctypename+T%3E+using+TVecPtr+%3D+vector%3CT%3E%3B%0A%0Atemplate+%3Ctypename+T%3E+using+TDynArray+%3D+unique_ptr%3CTVecPtr%3CT%3E%3E%3B%0A%0Atemplate+%3Ctypename+T,+typename...+Values%3E%0Aconstexpr+TDynArray%3CT%3E+DynArray(Values...+vals)+%7B%0A++return+make_unique%3CTVecPtr%3CT%3E%3E(vector%3CT%3E%7Bvals...%7D)%3B%0A%7D%0A%0Aint+main()+%7B%0A++const+auto+Strings+%3D+DynArray%3Cstring%3E(%0A++++%22Pascal!'s%22,%0A++++%22real%22,%0A++++%22dynamic%22,%0A++++%22arrays%22,%0A++++%22are%22,%0A++++%22much%22,%0A++++%22nicer!!%22%0A++)%3B%0A++for+(auto+S+:+*Strings)+%7B%0A++++cout+%3C%3C+S+%3C%3C+%22%5Cn%22%3B%0A++%7D%0A%7D'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:50.745098039215684,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:clang_trunk,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'1',trim:'1'),lang:c%2B%2B,libs:!(),options:'-O3',source:1),l:'5',n:'0',o:'x86-64+clang+(trunk)+(Editor+%231,+Compiler+%231)+C%2B%2B',t:'0')),k:49.254901960784316,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4> for a C++ example. Note how the function being marked constexpr doesn't actually do anything special in that particular case, and if you try to initialize the array with "constexpr auto" instead of "const auto", the whole program just won't compile.

Reply by email

Ben Grasset
to Marģers .:   ----- Reply to message ----- Subject: Re: [fpc-devel] XML node dump feature Date:…

On Tue, Jun 25, 2019 at 12:28 AM Marģers . via fpc-devel <

Show quoted text 6 lines
wrote: Sorry to say but, this should not work even with *pure* function. Typed constants are not truly constants.

It's no different at all from doing something like this, which does work, specifically because the compiler is (currently) able to evaluate it at compile time:

const I: LongInt = 2 * 2;

Reply by email

J. Gareth Moreton
to Jonas Maebe: That would definitely not be okay. Jonas

Maybe I'm misreading this, but does that mean you're not a fan of the "pure" directive, Jonas?

My reasoning for including it as a directive and it not being automatic is that having the compiler determine if a function is pure or not is going to be an expensive operation and will also potentially cause problems akin to what I mentioned in another topic in that the nodes will be exposed in the PPU files.

To explain, the first pass of "pure" would probably be to just look for nodes that would be illegal, such as referencing pointers or calling other functions that aren't pure themselves - this will catch most cases, but not all of them.  The second pass is whenever a pure function is called, attempting to run through the nodes like it were an interpreted language to calculate the result, but it might then hit an infinite loop, or something that is not infinite but takes a disproportionately long time to compute (e.g. the Ackermann function, which would necessitate an upper limit on nodes parsed)... or hits a division by zero error, for example.

All in all, it feels to me that declaring a function as pure is best left in the hands of the user rather than the compiler.

Gareth aka. Kit

Jonas Maebe wrote 15 lines
On 25/06/2019 21:23, denisgolovan wrote: >> Complex types are built from primitive types. And if FPC can verify the >> "pure" attribute is valid, then LLVM can probably also come up with it. >> In fact, if FPC can verify it is valid, it can also come up with it >> itself and add it as an annotation in the LLVM bitcode. > AFAIU, "pure" attribute is planned to be user's responsibility and that's probably ok. That would definitely not be okay. Jonas _______________________________________________ fpc-devel maillist - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

---
This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

Reply by email

Unknown author
to J. Gareth Moreton
J. Gareth Moreton wrote
Maybe I'm misreading this, but does that mean you're not a fan of the "pure" directive, Jonas?

i've been quietly reading along on all this "pure function" stuff since it was first brought up but somehow i've missed exactly what "pure" means... pure pascal? pure assembly? pure gold?

NOTE: No off-list assistance is given without prior approval. *Please keep mailing list traffic on the list unless* *a signed and pre-paid contract is in effect with us.*

Reply by email

J. Gareth Moreton
to Unknown author

https://en.wikipedia.org/wiki/Pure_function

In essence, a pure function will always return the same output for a given input, analogous to a mathematical function.

Gareth aka. Kit

Show quoted text 10 lines
On 6/26/19 1:48 AM, J. Gareth Moreton wrote: > Maybe I'm misreading this, but does that mean you're not a fan of the > "pure" directive, Jonas? i've been quietly reading along on all this "pure function" stuff since it was first brought up but somehow i've missed exactly what "pure" means... pure pascal? pure assembly? pure gold?

---
This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

Reply by email

Unknown author
to J. Gareth Moreton

Perhaps "DETERMINISTIC" (used in Oracle) is more accurate than "PURE".

https://docs.oracle.com/en/database/oracle/oracle-database/12.2/lnpls/DETERMINISTIC-clause.html#GUID-6AECC957-27CC-4334-9F43-0FBE88F92654

Petr Kohut

Earlier message 29 lines
-----Original Message----- From: fpc-devel On Behalf Of J. Gareth Moreton Sent: Wednesday, June 26, 2019 1:20 PM To: Subject: Re: [fpc-devel] XML node dump feature https://en.wikipedia.org/wiki/Pure_function In essence, a pure function will always return the same output for a given input, analogous to a mathematical function. Gareth aka. Kit On 26/06/2019 12:18, wrote: > On 6/26/19 1:48 AM, J. Gareth Moreton wrote: >> Maybe I'm misreading this, but does that mean you're not a fan of the >> "pure" directive, Jonas? > > > i've been quietly reading along on all this "pure function" stuff > since it was first brought up but somehow i've missed exactly what > "pure" means... pure pascal? pure assembly? pure gold? > > --- This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

Reply by email

Ben Grasset
to J. Gareth Moreton: Maybe I'm misreading this, but does that mean you're not a fan of the "pure" directive,…
AM J. Gareth Moreton wrote
Maybe I'm misreading this, but does that mean you're not a fan of the "pure" directive, Jonas?

It seems like if anything, he may have misunderstood that in this context, "in the hands of the user" is not intended to mean that people will be able to just mark absolutely anything as `pure` and have it magically work somehow.

I.E. as you've stated, functions marked `pure` will be checked, and the compiler will give an error if it turns out they cannot actually *be* `pure`.

In general though, IMO, something like `pure` that will be usable from all backends is certainly more useful on a longterm basis than relying directly on optimizations from the LLVM backend (which has rather limited platform support.)

Reply by email