Browse Source

* 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 -

Jonas Maebe 9 years ago
parent
commit
07856870bc
1 changed files with 155 additions and 146 deletions
  1. 155 146
      compiler/ncal.pas

+ 155 - 146
compiler/ncal.pas

@@ -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
-                  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);
+                maybecreateinlineparatemp(para);
               end;
               end;
             para := tcallparanode(para.right);
             para := tcallparanode(para.right);
           end;
           end;