فهرست منبع

* factored out all the tests for whether we need to put inline parameters
in temps (no functional changes)

git-svn-id: trunk@34286 -

Jonas Maebe 9 سال پیش
والد
کامیت
a3ca471d32
1فایلهای تغییر یافته به همراه119 افزوده شده و 98 حذف شده
  1. 119 98
      compiler/ncal.pas

+ 119 - 98
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;