With my experiments on i386 anx x86_64 (without the alignment changes) the complex record is always passed by reference, but without const, the function prologue then makes a copy of it on the function's local stack, which is then referenced to in the rest of the function.  Whether or not const is present or not, the same reference is passed into the function unmodified (the compiled assembly language is no different).

I think in a way, Florian and I have slightly different views.  I don't trust the compiler to make the most optimal code (i.e. a lazy compiler... I didn't want to say that Florian's compiler was inefficient until he himself said it!), so I try to give it hints where I can, and inserting "const" modifiers seems harmless enough since this has been a documented Pascal feature for decades, and most of the functions don't modify the parameter, so adding "const" just enforces it on the compiler's side.

Granted, I do seek to make improvements to the compiler where possible, and it's something I enjoy doing.  In the case of 'auto-const', I imagine it could be done at the node level, detecting that a parameter is only read from and never written to, but there may still be traps where you modify it without meaning to and causing inefficiencies.  Case in point, I had to make one small change to the "cth" function because it reused the parameter as a temporary variable.  Originally, it was this:

  function cth (z : complex) : complex;
    { hyberbolic complex tangent }
    { th(x) = sinh(x) / cosh(x) }
    { cosh(x) > 1 qq x }
    var temp: complex;
    begin
       temp := cch(z);
       z := csh(z);
       cth := z / temp;
    end;

I changed it to the following because specifying "const" caused a compiler error:

  function cth (const z : complex) : complex;
    { hyberbolic complex tangent }
    { th(x) = sinh(x) / cosh(x) }
    { cosh(x) > 1 qq x }
    var temp, hsinz : complex;
    begin
       temp := cch(z);
       hsinz := csh(z);
       cth := hsinz / temp;
    end;

I'm assuming there's a good reason as to why it can't simply be written as "cth := csh(z) / cch(z);" (and it looks easier to auto-inline), although currently that reason eludes me.

I don't think the compiler can be made smart and safe enough to auto-align something like the complex type to take full advantage of the System V ABI, and vectorcall is not the default Win64 calling convention (and the default convention is a little badly-designed if I'm allowed to say, since it doesn't vectorise anything at all).  Plus other platforms may have more restrictive memory availability and coarse alignment is not desired since it causes wastage.  Granted, when it comes to increased maintainability, the little tricks required to align the complex type while keeping the same field names is very tricky to understand and get correct (hence my suggestion of a distinct "align ##" modifier at the end of the type declaration, but that's another story).

I think the question of whether a micro-optimisation increases maintainability is fairly subjective and can only be determined on a case-by-case basis.  In my mind, if someone has done the optimisation and the code is still relatively clean, then it's okay to merge so long as everyone accepts it and it's fully tested.

Gareth aka. Kit


On 26/10/2019 18:02, Sven Barth via fpc-devel wrote:
Am 26.10.2019 um 18:51 schrieb J. Gareth Moreton:
The "const" suggestion was made by a third party, and while I went out of my way to ensure the functions aren't changed in Pascal code, Florian pointed out that it could break existing assembler code. Maybe I'm being a bit stubborn or unreasonable, I'm not sure, but in my eyes, using assembly language to directly call the uComplex functions and operators seems rather unrealistic.  I figured if you're writing in assembly language, especially if you're using vector registers, you'd be using your own code to play around with complex numbers.  Plus I figured that if you're developing on a non-x86_64 platform, the only thing that's different are the 'const' modifiers, which I don't think changes the way you actually call the function, regardless of platform.  Am I right in this?

It totally depends on how "const" is implemented for the particular target. On some there might not be any difference on others there might be a similar difference as for x86, namely that something is passed as a reference instead of a copy.

I guess a more fundamental question I should ask, and this might be terribly naïve of me, is this: when you call some function F(x: TType), is there a situation where calling F(const x: TType) produces different machine code or where a particular actual parameter becomes illegal? Note I'm talking about how you call the function, not how the function itself is compiled.

Didn't you provide the example yourself with your changes to the uComplex unit? There are cases (especially with records) where "x" is passed as a copy on the stack and "const x" is passed as a reference.

Regards,
Sven
_______________________________________________
fpc-devel maillist  -  fpc-devel@lists.freepascal.org
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

_______________________________________________
fpc-devel maillist  -  fpc-devel@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply via email to