[fpc-devel] Question on updating FPC packages

J. Gareth Moreton gareth at moreton-family.com
Sat Oct 26 20:03:46 CEST 2019

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;
        temp := cch(z);
        z := csh(z);
        cth := z / temp;

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;
        temp := cch(z);
        hsinz := csh(z);
        cth := hsinz / temp;

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 at lists.freepascal.org
> https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

This email has been checked for viruses by Avast antivirus software.

More information about the fpc-devel mailing list