This is the code block in question (ncnv.pas, starting at line 3397) - if anyone can explain why it has to be set up this way, or add comments to the code, I will be most grateful (it's run for the following node types: subn, addn, muln, divn, modn, xorn, andn, orn, shln, shrn):
exclude(n.flags,nf_internal);
if not forceunsigned and
is_signed(n.resultdef) then
begin
originaldivtree:=nil;
if n.nodetype in [divn,modn] then
originaldivtree:=n.getcopy;
doremoveinttypeconvs(level+1,tbinarynode(n).left,signedtype,false,signedtype,unsignedtype);
doremoveinttypeconvs(level+1,tbinarynode(n).right,signedtype,false,signedtype,unsignedtype);
n.resultdef:=signedtype;
if n.nodetype in [divn,modn] then
begin
newblock:=internalstatements(newstatements);
tempnode:=ctempcreatenode.create(n.resultdef,n.resultdef.size,tt_persistent,true);
addstatement(newstatements,tempnode);
addstatement(newstatements,cifnode.create_internal(
caddnode.create_internal(equaln,tbinarynode(n).right.getcopy,cordconstnode.create(-1,n.resultdef,false)),
cassignmentnode.create_internal(
ctemprefnode.create(tempnode),
cmoddivnode.create(n.nodetype,tbinarynode(originaldivtree).left.getcopy,cordconstnode.create(-1,tbinarynode(originaldivtree).right.resultdef,false))
),
cassignmentnode.create_internal(
ctemprefnode.create(tempnode),n
)
)
);
addstatement(newstatements,ctempdeletenode.create_normal_temp(tempnode));
addstatement(newstatements,ctemprefnode.create(tempnode));
n:=newblock;
do_typecheckpass(n);
originaldivtree.free;
end;
end
(the new division/modulus by -1 is then converted elsewhere)
Kit
J. Gareth Moreton wrote 145 lines
P.S. I found the code that adds the conditional checks; it's
"doremoveinttypeconvs" in the ncnv unit. However, it's very unclear
as to WHY it's doing it as there's no comments around the code block.
Kit
On 11/05/2023 15:39, J. Gareth Moreton via fpc-devel wrote:
> It does seem odd. In a practical sense, the only time I can see -1
> being a common input among other random numbers is if it's an error
> value, in which case you would most likely do special handling rather
> than pass it through a division operation. With the slowdown that
> comes from additional branch prediction, it just seems like
> unnecessary fluff, but I need to double-check to see if there's a
> very good reason behind their generation (if it's a platform-specific
> problem, it should be moved to that platform's specific first pass)
> Now I just need to find out where those nodes are generated - they're
> proving elusive!
>
> Note that using constant divisors uses a different optimisation, so
> this only applies to variable divisors.
>
> Kit
>
> On 11/05/2023 12:07, Stefan Glienke via fpc-devel wrote:
>> Looks like a rather disadvantageous way to avoid the idiv
>> instruction because x div -1 = -x and x mod -1 = 0.
>>
>> I ran a quick benchmark doing a lot of integer divisions where
>> sometimes (randomly) the divisor was -1. When the occurence was rare
>> enough (~5%) the performance was not impacted, the higher the
>> occurence of -1 was the slower it became to almost half as fast.
>> Only when less than 5% of the divisors were *not* -1 the performance
>> was better up to twice as fast when all divisors were -1. Of couse
>> ymmv as it depends on the CPU and the branch predictor behavior but
>> it shows that this "optimization" is hardly any good.
>>
>> I cannot think of a realistic case where 95% of your divisors are -1
>> and you really need to save those few extra cycles of calling idiv.
>>
>>> On 11/05/2023 11:04 CEST J. Gareth Moreton via fpc-devel
>>> wrote:
>>>
>>> Hi everyone,
>>>
>>> I need to ask a question about how division nodes are set up (I'm
>>> looking at possible optimisation techniques). I've written the
>>> following procedure:
>>>
>>> procedure DoDivMod(N, D: Integer; out Q, R: Integer);
>>> begin
>>> Q := N div D;
>>> R := N mod D;
>>> end;
>>>
>>> Fairly simple and to the point. However, even before the first node
>>> pass, the following node tree is generated for an integer division
>>> operation:
>>>
>>> <statementn pos="24,10">
>>> <ifn resultdef="$void" pos="24,10" flags="nf_internal">
>>> <condition>
>>> <equaln resultdef="Boolean" pos="24,10"
>>> flags="nf_internal">
>>> <loadn resultdef="LongInt" pos="24,14">
>>> <symbol>D</symbol>
>>> </loadn>
>>> <ordconstn resultdef="LongInt" pos="24,10"
>>> rangecheck="FALSE">
>>> <value>-1</value>
>>> </ordconstn>
>>> </equaln>
>>> </condition>
>>> <then>
>>> <assignn resultdef="$void" pos="24,10" flags="nf_internal">
>>> <temprefn resultdef="LongInt" pos="24,10"
>>> flags="nf_write"
>>> id="$7C585E10">
>>> <typedef>LongInt</typedef>
>>> <tempflags>ti_may_be_in_reg</tempflags>
>>> <temptype>tt_persistent</temptype>
>>> </temprefn>
>>> <unaryminusn resultdef="LongInt" pos="24,10">
>>> <loadn resultdef="LongInt" pos="24,8">
>>> <symbol>N</symbol>
>>> </loadn>
>>> </unaryminusn>
>>> </assignn>
>>> </then>
>>> <else>
>>> <assignn resultdef="$void" pos="24,10" flags="nf_internal">
>>> <temprefn resultdef="LongInt" pos="24,10"
>>> flags="nf_write"
>>> id="$7C585E10">
>>> <typedef>LongInt</typedef>
>>> <tempflags>ti_may_be_in_reg</tempflags>
>>> <temptype>tt_persistent</temptype>
>>> </temprefn>
>>> <divn resultdef="LongInt" pos="24,10">
>>> <loadn resultdef="LongInt" pos="24,8">
>>> <symbol>N</symbol>
>>> </loadn>
>>> <loadn resultdef="LongInt" pos="24,14">
>>> <symbol>D</symbol>
>>> </loadn>
>>> </divn>
>>> </assignn>
>>> </else>
>>> </ifn>
>>> </statementn>
>>>
>>> Something similar is made for "mod" as well. I have to ask
>>> though... is
>>> it really necessary to check to see if the divisor is -1 and have a
>>> distinct assignment for it? It's a bit of a rare edge case that
>>> usually
>>> just slows things down since it tends to add a comparison and a
>>> conditional jump to the final assembly language. Is there some
>>> anomalous behaviour to a processor's division routine if the
>>> divisor is -1?
>>>
>>> At the very least, would it be possible to remove the conditional
>>> check
>>> when compiling under -Os?
>>>
>>> (I intend to see if it's possible to merge "N div D" and "N mod D" on
>>> x86, and possibly other processors that have a combined DIV/MOD
>>> operator).
>>>
>>> Kit
>>>
>>> _______________________________________________
>>> fpc-devel maillist -
>>>
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
>> _______________________________________________
>> fpc-devel maillist -
>>
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
>>
> _______________________________________________
> fpc-devel maillist -
>
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
>
_______________________________________________
fpc-devel maillist -
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel