mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-13 22:20:23 +02:00
* factored out code to determine whether we should put an inline parameter
in a temp or not (no functional changes) git-svn-id: trunk@34283 -
This commit is contained in:
parent
b17f90d183
commit
07856870bc
@ -99,6 +99,7 @@ interface
|
|||||||
inlinelocals : TFPObjectList;
|
inlinelocals : TFPObjectList;
|
||||||
inlineinitstatement,
|
inlineinitstatement,
|
||||||
inlinecleanupstatement : tstatementnode;
|
inlinecleanupstatement : tstatementnode;
|
||||||
|
procedure maybecreateinlineparatemp(para: tcallparanode);
|
||||||
procedure createinlineparas;
|
procedure createinlineparas;
|
||||||
procedure wrapcomplexinlinepara(para: tcallparanode); virtual;
|
procedure wrapcomplexinlinepara(para: tcallparanode); virtual;
|
||||||
function replaceparaload(var n: tnode; arg: pointer): foreachnoderesult;
|
function replaceparaload(var n: tnode; arg: pointer): foreachnoderesult;
|
||||||
@ -4514,19 +4515,167 @@ implementation
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure tcallnode.createinlineparas;
|
procedure tcallnode.maybecreateinlineparatemp(para: tcallparanode);
|
||||||
var
|
var
|
||||||
para: tcallparanode;
|
|
||||||
tempnode: ttempcreatenode;
|
tempnode: ttempcreatenode;
|
||||||
n,
|
|
||||||
realtarget: tnode;
|
realtarget: tnode;
|
||||||
paracomplexity: longint;
|
paracomplexity: longint;
|
||||||
pushconstaddr: boolean;
|
pushconstaddr: boolean;
|
||||||
trytotakeaddress : Boolean;
|
trytotakeaddress : boolean;
|
||||||
|
begin
|
||||||
|
{ determine how a parameter is passed to the inlined body
|
||||||
|
There are three options:
|
||||||
|
- insert the node tree of the callparanode directly
|
||||||
|
If a parameter is used only once, this is the best option if we can do so
|
||||||
|
- get the address of the argument, store it in a temp and insert a dereference to this temp
|
||||||
|
If the node tree cannot be inserted directly, taking the address of the argument and using it
|
||||||
|
is the second best option, but even this is not always possible
|
||||||
|
- assign the value of the argument to a newly created temp
|
||||||
|
This is the fall back which works always
|
||||||
|
Notes:
|
||||||
|
- we need to take care that we use the type of the defined parameter and not of the
|
||||||
|
passed parameter, because these can be different in case of a formaldef (PFV)
|
||||||
|
}
|
||||||
|
|
||||||
|
{ pre-compute some values }
|
||||||
|
paracomplexity:=node_complexity(para.left);
|
||||||
|
if para.parasym.varspez=vs_const then
|
||||||
|
pushconstaddr:=paramanager.push_addr_param(vs_const,para.parasym.vardef,procdefinition.proccalloption)
|
||||||
|
else
|
||||||
|
pushconstaddr:=false;
|
||||||
|
realtarget:=actualtargetnode(@para.left)^;
|
||||||
|
|
||||||
|
{ if the parameter is "complex", try to take the address
|
||||||
|
of the parameter expression, store it in a temp and replace
|
||||||
|
occurrences of the parameter with dereferencings of this
|
||||||
|
temp
|
||||||
|
}
|
||||||
|
trytotakeaddress:=
|
||||||
|
{ don't create a temp. for function results }
|
||||||
|
not(nf_is_funcret in realtarget.flags) and
|
||||||
|
{ this makes only sense if the parameter is reasonable complex else inserting directly is a better solution }
|
||||||
|
(
|
||||||
|
(paracomplexity>2) or
|
||||||
|
{ don't create a temp. for the often seen case that p^ is passed to a var parameter }
|
||||||
|
((paracomplexity>1) and
|
||||||
|
not((realtarget.nodetype=derefn) and (para.parasym.varspez in [vs_var,vs_out,vs_constref])) and
|
||||||
|
not((realtarget.nodetype=loadn) and tloadnode(realtarget).is_addr_param_load)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
{ check if we have to create a temp, assign the parameter's
|
||||||
|
contents to that temp and then substitute the parameter
|
||||||
|
with the temp everywhere in the function }
|
||||||
|
if
|
||||||
|
((tparavarsym(para.parasym).varregable in [vr_none,vr_addr]) and
|
||||||
|
not(para.left.expectloc in [LOC_REFERENCE,LOC_CREFERENCE])) or
|
||||||
|
{ we can't assign to formaldef temps }
|
||||||
|
((para.parasym.vardef.typ<>formaldef) and
|
||||||
|
(
|
||||||
|
{ can we take the address of the argument? }
|
||||||
|
(trytotakeaddress and not(para.left.expectloc in [LOC_REFERENCE,LOC_CREFERENCE])) or
|
||||||
|
(trytotakeaddress and
|
||||||
|
(not valid_for_addr(para.left,false) or
|
||||||
|
(para.left.nodetype = calln) or
|
||||||
|
is_constnode(para.left))) or
|
||||||
|
{ we do not need to create a temp for value parameters }
|
||||||
|
{ which are not modified in the inlined function }
|
||||||
|
{ const parameters can get vs_readwritten if their }
|
||||||
|
{ address is taken }
|
||||||
|
((((para.parasym.varspez = vs_value) and
|
||||||
|
(para.parasym.varstate in [vs_initialised,vs_declared,vs_read])) or
|
||||||
|
{ in case of const, this is only necessary if the
|
||||||
|
variable would be passed by value normally and if it is modified or if
|
||||||
|
there is such a variable somewhere in an expression }
|
||||||
|
((para.parasym.varspez = vs_const) and
|
||||||
|
(not pushconstaddr))) and
|
||||||
|
{ however, if we pass a global variable, an object field or}
|
||||||
|
{ an expression containing a pointer dereference as }
|
||||||
|
{ parameter, this value could be modified in other ways as }
|
||||||
|
{ well and in such cases create a temp to be on the safe }
|
||||||
|
{ side }
|
||||||
|
foreachnodestatic(para.left,@nonlocalvars,pointer(symtableproc))) or
|
||||||
|
{ value parameters of which we know they are modified by }
|
||||||
|
{ definition have to be copied to a temp }
|
||||||
|
{ the same goes for cases of "x:=f(x)" where x is passed }
|
||||||
|
{ as value parameter to f(), at least if we optimized }
|
||||||
|
{ invocation by setting the funcretnode to x to avoid }
|
||||||
|
{ assignment afterwards (since x may be read inside the }
|
||||||
|
{ function after it modified result==x) }
|
||||||
|
((para.parasym.varspez = vs_value) and
|
||||||
|
(not(para.parasym.varstate in [vs_initialised,vs_declared,vs_read]) or
|
||||||
|
(assigned(aktassignmentnode) and
|
||||||
|
(aktassignmentnode.right=self) and
|
||||||
|
(nf_assign_done_in_right in aktassignmentnode.flags) and
|
||||||
|
actualtargetnode(@aktassignmentnode.left)^.isequal(actualtargetnode(@para.left)^)))) or
|
||||||
|
{ the compiler expects that it can take the address of parameters passed by reference in
|
||||||
|
the case of const so we can't replace the node simply by a constant node
|
||||||
|
When playing with this code, ensure that
|
||||||
|
function f(const a,b : longint) : longint;inline;
|
||||||
|
begin
|
||||||
|
result:=a*b;
|
||||||
|
end;
|
||||||
|
|
||||||
|
[...]
|
||||||
|
...:=f(10,20));
|
||||||
|
[...]
|
||||||
|
|
||||||
|
is still folded. (FK)
|
||||||
|
}
|
||||||
|
((para.parasym.varspez = vs_const) and
|
||||||
|
{ const para's can get vs_readwritten if their address }
|
||||||
|
{ is taken -> in case they are not passed by reference, }
|
||||||
|
{ to keep the same behaviour as without inlining we have }
|
||||||
|
{ to make a copy in case the originally passed parameter }
|
||||||
|
{ value gets changed inside the callee }
|
||||||
|
((not pushconstaddr and
|
||||||
|
(para.parasym.varstate = vs_readwritten)
|
||||||
|
) or
|
||||||
|
{ call-by-reference const's may need to be passed by }
|
||||||
|
{ reference to function called in the inlined code }
|
||||||
|
(pushconstaddr and
|
||||||
|
not valid_for_addr(para.left,false))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) then
|
||||||
|
begin
|
||||||
|
{ don't create a new temp unnecessarily, but make sure we
|
||||||
|
do create a new one if the old one could be a regvar and
|
||||||
|
the new one cannot be one }
|
||||||
|
if not(tparavarsym(para.parasym).varspez in [vs_out,vs_var]) and (((para.left.nodetype<>temprefn) or
|
||||||
|
(((tparavarsym(para.parasym).varregable in [vr_none,vr_addr])) and
|
||||||
|
(ti_may_be_in_reg in ttemprefnode(para.left).tempinfo^.flags)))) then
|
||||||
|
begin
|
||||||
|
tempnode := ctempcreatenode.create(para.parasym.vardef,para.parasym.vardef.size,
|
||||||
|
tt_persistent,tparavarsym(para.parasym).is_regvar(false));
|
||||||
|
addstatement(inlineinitstatement,tempnode);
|
||||||
|
|
||||||
|
if localvartrashing <> -1 then
|
||||||
|
cnodeutils.maybe_trash_variable(inlineinitstatement,para.parasym,ctemprefnode.create(tempnode));
|
||||||
|
|
||||||
|
addstatement(inlinecleanupstatement,ctempdeletenode.create(tempnode));
|
||||||
|
|
||||||
|
addstatement(inlineinitstatement,cassignmentnode.create(ctemprefnode.create(tempnode),
|
||||||
|
para.left));
|
||||||
|
para.left := ctemprefnode.create(tempnode);
|
||||||
|
{ inherit addr_taken flag }
|
||||||
|
if (tabstractvarsym(para.parasym).addr_taken) then
|
||||||
|
include(tempnode.tempinfo^.flags,ti_addr_taken);
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else if trytotakeaddress then
|
||||||
|
wrapcomplexinlinepara(para);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure tcallnode.createinlineparas;
|
||||||
|
var
|
||||||
|
para: tcallparanode;
|
||||||
|
n: tnode;
|
||||||
begin
|
begin
|
||||||
{ parameters }
|
{ parameters }
|
||||||
para := tcallparanode(left);
|
para := tcallparanode(left);
|
||||||
pushconstaddr := false;
|
|
||||||
while assigned(para) do
|
while assigned(para) do
|
||||||
begin
|
begin
|
||||||
if (para.parasym.typ = paravarsym) and
|
if (para.parasym.typ = paravarsym) and
|
||||||
@ -4544,147 +4693,7 @@ implementation
|
|||||||
|
|
||||||
firstpass(para.left);
|
firstpass(para.left);
|
||||||
|
|
||||||
{ determine how a parameter is passed to the inlined body
|
maybecreateinlineparatemp(para);
|
||||||
There are three options:
|
|
||||||
- insert the node tree of the callparanode directly
|
|
||||||
If a parameter is used only once, this is the best option if we can do so
|
|
||||||
- get the address of the argument, store it in a temp and insert a dereference to this temp
|
|
||||||
If the node tree cannot be inserted directly, taking the address of the argument and using it
|
|
||||||
is the second best option, but even this is not always possible
|
|
||||||
- assign the value of the argument to a newly created temp
|
|
||||||
This is the fall back which works always
|
|
||||||
Notes:
|
|
||||||
- we need to take care that we use the type of the defined parameter and not of the
|
|
||||||
passed parameter, because these can be different in case of a formaldef (PFV)
|
|
||||||
}
|
|
||||||
|
|
||||||
{ pre-compute some values }
|
|
||||||
paracomplexity:=node_complexity(para.left);
|
|
||||||
if para.parasym.varspez=vs_const then
|
|
||||||
pushconstaddr:=paramanager.push_addr_param(vs_const,para.parasym.vardef,procdefinition.proccalloption);
|
|
||||||
realtarget:=actualtargetnode(@para.left)^;
|
|
||||||
|
|
||||||
{ if the parameter is "complex", try to take the address
|
|
||||||
of the parameter expression, store it in a temp and replace
|
|
||||||
occurrences of the parameter with dereferencings of this
|
|
||||||
temp
|
|
||||||
}
|
|
||||||
trytotakeaddress:=
|
|
||||||
{ don't create a temp. for function results }
|
|
||||||
not(nf_is_funcret in realtarget.flags) and
|
|
||||||
{ this makes only sense if the parameter is reasonable complex else inserting directly is a better solution }
|
|
||||||
(
|
|
||||||
(paracomplexity>2) or
|
|
||||||
{ don't create a temp. for the often seen case that p^ is passed to a var parameter }
|
|
||||||
((paracomplexity>1) and
|
|
||||||
not((realtarget.nodetype=derefn) and (para.parasym.varspez in [vs_var,vs_out,vs_constref])) and
|
|
||||||
not((realtarget.nodetype=loadn) and tloadnode(realtarget).is_addr_param_load)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
{ check if we have to create a temp, assign the parameter's
|
|
||||||
contents to that temp and then substitute the parameter
|
|
||||||
with the temp everywhere in the function }
|
|
||||||
if
|
|
||||||
((tparavarsym(para.parasym).varregable in [vr_none,vr_addr]) and
|
|
||||||
not(para.left.expectloc in [LOC_REFERENCE,LOC_CREFERENCE])) or
|
|
||||||
{ we can't assign to formaldef temps }
|
|
||||||
((para.parasym.vardef.typ<>formaldef) and
|
|
||||||
(
|
|
||||||
{ can we take the address of the argument? }
|
|
||||||
(trytotakeaddress and not(para.left.expectloc in [LOC_REFERENCE,LOC_CREFERENCE])) or
|
|
||||||
(trytotakeaddress and
|
|
||||||
(not valid_for_addr(para.left,false) or
|
|
||||||
(para.left.nodetype = calln) or
|
|
||||||
is_constnode(para.left))) or
|
|
||||||
{ we do not need to create a temp for value parameters }
|
|
||||||
{ which are not modified in the inlined function }
|
|
||||||
{ const parameters can get vs_readwritten if their }
|
|
||||||
{ address is taken }
|
|
||||||
((((para.parasym.varspez = vs_value) and
|
|
||||||
(para.parasym.varstate in [vs_initialised,vs_declared,vs_read])) or
|
|
||||||
{ in case of const, this is only necessary if the
|
|
||||||
variable would be passed by value normally and if it is modified or if
|
|
||||||
there is such a variable somewhere in an expression }
|
|
||||||
((para.parasym.varspez = vs_const) and
|
|
||||||
(not pushconstaddr))) and
|
|
||||||
{ however, if we pass a global variable, an object field or}
|
|
||||||
{ an expression containing a pointer dereference as }
|
|
||||||
{ parameter, this value could be modified in other ways as }
|
|
||||||
{ well and in such cases create a temp to be on the safe }
|
|
||||||
{ side }
|
|
||||||
foreachnodestatic(para.left,@nonlocalvars,pointer(symtableproc))) or
|
|
||||||
{ value parameters of which we know they are modified by }
|
|
||||||
{ definition have to be copied to a temp }
|
|
||||||
{ the same goes for cases of "x:=f(x)" where x is passed }
|
|
||||||
{ as value parameter to f(), at least if we optimized }
|
|
||||||
{ invocation by setting the funcretnode to x to avoid }
|
|
||||||
{ assignment afterwards (since x may be read inside the }
|
|
||||||
{ function after it modified result==x) }
|
|
||||||
((para.parasym.varspez = vs_value) and
|
|
||||||
(not(para.parasym.varstate in [vs_initialised,vs_declared,vs_read]) or
|
|
||||||
(assigned(aktassignmentnode) and
|
|
||||||
(aktassignmentnode.right=self) and
|
|
||||||
(nf_assign_done_in_right in aktassignmentnode.flags) and
|
|
||||||
actualtargetnode(@aktassignmentnode.left)^.isequal(actualtargetnode(@para.left)^)))) or
|
|
||||||
{ the compiler expects that it can take the address of parameters passed by reference in
|
|
||||||
the case of const so we can't replace the node simply by a constant node
|
|
||||||
When playing with this code, ensure that
|
|
||||||
function f(const a,b : longint) : longint;inline;
|
|
||||||
begin
|
|
||||||
result:=a*b;
|
|
||||||
end;
|
|
||||||
|
|
||||||
[...]
|
|
||||||
...:=f(10,20));
|
|
||||||
[...]
|
|
||||||
|
|
||||||
is still folded. (FK)
|
|
||||||
}
|
|
||||||
((para.parasym.varspez = vs_const) and
|
|
||||||
{ const para's can get vs_readwritten if their address }
|
|
||||||
{ is taken -> in case they are not passed by reference, }
|
|
||||||
{ to keep the same behaviour as without inlining we have }
|
|
||||||
{ to make a copy in case the originally passed parameter }
|
|
||||||
{ value gets changed inside the callee }
|
|
||||||
((not pushconstaddr and
|
|
||||||
(para.parasym.varstate = vs_readwritten)
|
|
||||||
) or
|
|
||||||
{ call-by-reference const's may need to be passed by }
|
|
||||||
{ reference to function called in the inlined code }
|
|
||||||
(pushconstaddr and
|
|
||||||
not valid_for_addr(para.left,false))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) then
|
|
||||||
begin
|
|
||||||
{ don't create a new temp unnecessarily, but make sure we
|
|
||||||
do create a new one if the old one could be a regvar and
|
|
||||||
the new one cannot be one }
|
|
||||||
if not(tparavarsym(para.parasym).varspez in [vs_out,vs_var]) and (((para.left.nodetype<>temprefn) or
|
|
||||||
(((tparavarsym(para.parasym).varregable in [vr_none,vr_addr])) and
|
|
||||||
(ti_may_be_in_reg in ttemprefnode(para.left).tempinfo^.flags)))) then
|
|
||||||
begin
|
|
||||||
tempnode := ctempcreatenode.create(para.parasym.vardef,para.parasym.vardef.size,
|
|
||||||
tt_persistent,tparavarsym(para.parasym).is_regvar(false));
|
|
||||||
addstatement(inlineinitstatement,tempnode);
|
|
||||||
|
|
||||||
if localvartrashing <> -1 then
|
|
||||||
cnodeutils.maybe_trash_variable(inlineinitstatement,para.parasym,ctemprefnode.create(tempnode));
|
|
||||||
|
|
||||||
addstatement(inlinecleanupstatement,ctempdeletenode.create(tempnode));
|
|
||||||
|
|
||||||
addstatement(inlineinitstatement,cassignmentnode.create(ctemprefnode.create(tempnode),
|
|
||||||
para.left));
|
|
||||||
para.left := ctemprefnode.create(tempnode);
|
|
||||||
{ inherit addr_taken flag }
|
|
||||||
if (tabstractvarsym(para.parasym).addr_taken) then
|
|
||||||
include(tempnode.tempinfo^.flags,ti_addr_taken);
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if trytotakeaddress then
|
|
||||||
wrapcomplexinlinepara(para);
|
|
||||||
end;
|
end;
|
||||||
para := tcallparanode(para.right);
|
para := tcallparanode(para.right);
|
||||||
end;
|
end;
|
||||||
|
Loading…
Reference in New Issue
Block a user