* factored out all the tests for whether we need to put inline parameters

in temps (no functional changes)

git-svn-id: trunk@34286 -
This commit is contained in:
Jonas Maebe 2016-08-12 13:35:48 +00:00
parent 8d1e8ea203
commit a3ca471d32

View File

@ -4529,6 +4529,100 @@ implementation
realtarget: tnode;
paracomplexity: longint;
pushconstaddr: boolean;
function needtemp: boolean;
begin
{ We need a temp if the passed value will not be in memory, while
the parameter inside the routine must be in memory }
if (tparavarsym(para.parasym).varregable in [vr_none,vr_addr]) and
not(para.left.expectloc in [LOC_REFERENCE,LOC_CREFERENCE]) then
exit(true);
{ We cannot create a formaldef temp and assign something to it }
if para.parasym.vardef.typ=formaldef then
exit(false);
{ We try to handle complex expressions later by taking their address
and storing this address in a temp (which is then dereferenced when
the value is used; that doesn't work if we cannot take the address
of the expression though, in which case we store the result of the
expression in a temp }
if (complexpara and not(para.left.expectloc in [LOC_REFERENCE,LOC_CREFERENCE]) or
(complexpara and
(not valid_for_addr(para.left,false) or
(para.left.nodetype=calln) or
is_constnode(para.left)))) then
exit(true);
{ Normally, we do not need to create a temp for value parameters that
are not modified in the inlined function, and neither for const
parameters that are passed by value.
However, if we pass a global variable, an object field, a variable
whose address has been taken, or an expression containing a pointer
dereference as parameter, this value could be modified in other ways
as well (even inside the callee) and in such cases we still create a
temp to be on the safe side.
We *must not* create a temp for global variables passed by
reference to a const parameter, because if not inlined then any
changes to the original value will also be visible in the callee
(although this is technically undefined behaviour, since with
"const" the programmer tells the compiler this argument will not
change). }
if (((para.parasym.varspez=vs_value) and
(para.parasym.varstate in [vs_initialised,vs_declared,vs_read])) or
((para.parasym.varspez=vs_const) and
not pushconstaddr)) and
foreachnodestatic(para.left,@nonlocalvars,pointer(symtableproc)) then
exit(true);
{ 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 an
assignment afterwards (since x may be read inside the function after
it modified result==x) }
if (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)^))) then
exit(true);
{ 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)
}
if (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
exit(true);
result:=false;
end;
begin
result:=false;
{ determine how a parameter is passed to the inlined body
@ -4553,15 +4647,15 @@ implementation
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
{ 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
}
complexpara:=
{ 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 }
{ this makes only sense if the parameter is reasonably complex,
otherwise 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 }
@ -4571,106 +4665,33 @@ implementation
)
);
{ We don't need temps for parameters that are already temps, except if
the passed temp could be put in a regvar while the parameter inside
the routine cannot be (e.g., because its address is taken in the
routine) }
if (para.left.nodetype=temprefn) and
(not(ti_may_be_in_reg in ttemprefnode(para.left).tempinfo^.flags) or
not(tparavarsym(para.parasym).varregable in [vr_none,vr_addr])) then
exit;
{ 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? }
(complexpara and not(para.left.expectloc in [LOC_REFERENCE,LOC_CREFERENCE])) or
(complexpara 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
if needtemp 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);
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(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);
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;
result:=true;
end
end;