From a3ca471d326422a1f152b8885bea2d4e7d09da79 Mon Sep 17 00:00:00 2001 From: Jonas Maebe Date: Fri, 12 Aug 2016 13:35:48 +0000 Subject: [PATCH] * factored out all the tests for whether we need to put inline parameters in temps (no functional changes) git-svn-id: trunk@34286 - --- compiler/ncal.pas | 217 +++++++++++++++++++++++++--------------------- 1 file changed, 119 insertions(+), 98 deletions(-) diff --git a/compiler/ncal.pas b/compiler/ncal.pas index bcbf6b97f4..5ec99447dd 100644 --- a/compiler/ncal.pas +++ b/compiler/ncal.pas @@ -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;