Browse Source

* use destination location instead of a temp when constructing sets, resolves #40384

florian 1 year ago
parent
commit
06fc6f1e6b
1 changed files with 94 additions and 49 deletions
  1. 94 49
      compiler/nadd.pas

+ 94 - 49
compiler/nadd.pas

@@ -3616,6 +3616,7 @@ implementation
         tempn: tnode;
         tempn: tnode;
         newstatement : tstatementnode;
         newstatement : tstatementnode;
         temp    : ttempcreatenode;
         temp    : ttempcreatenode;
+        no_temp: Boolean;
       begin
       begin
         result:=nil;
         result:=nil;
 
 
@@ -3652,40 +3653,31 @@ implementation
             end;
             end;
           addn:
           addn:
             begin
             begin
+              { can we directly write into the result? }
+              no_temp:=assigned(aktassignmentnode) and
+                (aktassignmentnode.right=self) and
+                (aktassignmentnode.left.resultdef=self.resultdef) and
+                valid_for_var(aktassignmentnode.left,false);
+
               { optimize first loading of a set }
               { optimize first loading of a set }
               if (right.nodetype=setelementn) and
               if (right.nodetype=setelementn) and
                   not(assigned(tsetelementnode(right).right)) and
                   not(assigned(tsetelementnode(right).right)) and
                   is_emptyset(left) then
                   is_emptyset(left) then
                 begin
                 begin
-                  result:=internalstatements(newstatement);
-
-                  { create temp for result }
-                  temp:=ctempcreatenode.create(resultdef,resultdef.size,tt_persistent,true);
-                  addstatement(newstatement,temp);
-
                   { adjust for set base }
                   { adjust for set base }
                   tsetelementnode(right).left:=caddnode.create(subn,
                   tsetelementnode(right).left:=caddnode.create(subn,
                     ctypeconvnode.create_internal(tsetelementnode(right).left,sinttype),
                     ctypeconvnode.create_internal(tsetelementnode(right).left,sinttype),
                     cordconstnode.create(tsetdef(resultdef).setbase,sinttype,false));
                     cordconstnode.create(tsetdef(resultdef).setbase,sinttype,false));
 
 
-                  addstatement(newstatement,ccallnode.createintern('fpc_varset_create_element',
-                    ccallparanode.create(ctemprefnode.create(temp),
-                    ccallparanode.create(cordconstnode.create(resultdef.size,sinttype,false),
-                    ccallparanode.create(tsetelementnode(right).left,nil))))
-                  );
-
-                  { the last statement should return the value as
-                    location and type, this is done be referencing the
-                    temp and converting it first from a persistent temp to
-                    normal temp }
-                  addstatement(newstatement,ctempdeletenode.create_normal_temp(temp));
-                  addstatement(newstatement,ctemprefnode.create(temp));
-
-                  tsetelementnode(right).left := nil;
-                end
-              else
-                begin
-                  if right.nodetype=setelementn then
+                  if no_temp then
+                    begin
+                      result:=ccallnode.createintern('fpc_varset_create_element',
+                        ccallparanode.create(aktassignmentnode.left.getcopy,
+                        ccallparanode.create(cordconstnode.create(resultdef.size,sinttype,false),
+                        ccallparanode.create(tsetelementnode(right).left,nil))));
+                      include(aktassignmentnode.assignmentnodeflags,anf_assign_done_in_right);
+                    end
+                  else
                     begin
                     begin
                       result:=internalstatements(newstatement);
                       result:=internalstatements(newstatement);
 
 
@@ -3693,43 +3685,96 @@ implementation
                       temp:=ctempcreatenode.create(resultdef,resultdef.size,tt_persistent,true);
                       temp:=ctempcreatenode.create(resultdef,resultdef.size,tt_persistent,true);
                       addstatement(newstatement,temp);
                       addstatement(newstatement,temp);
 
 
+                      addstatement(newstatement,ccallnode.createintern('fpc_varset_create_element',
+                        ccallparanode.create(ctemprefnode.create(temp),
+                        ccallparanode.create(cordconstnode.create(resultdef.size,sinttype,false),
+                        ccallparanode.create(tsetelementnode(right).left,nil))))
+                      );
+
+                      { the last statement should return the value as
+                        location and type, this is done be referencing the
+                        temp and converting it first from a persistent temp to
+                        normal temp }
+                      addstatement(newstatement,ctempdeletenode.create_normal_temp(temp));
+                      addstatement(newstatement,ctemprefnode.create(temp));
+                    end;
+                  tsetelementnode(right).left:=nil;
+                end
+              else
+                begin
+                  if right.nodetype=setelementn then
+                    begin
                       { adjust for set base }
                       { adjust for set base }
                       tsetelementnode(right).left:=caddnode.create(subn,
                       tsetelementnode(right).left:=caddnode.create(subn,
                         ctypeconvnode.create_internal(tsetelementnode(right).left,sinttype),
                         ctypeconvnode.create_internal(tsetelementnode(right).left,sinttype),
                         cordconstnode.create(tsetdef(resultdef).setbase,sinttype,false));
                         cordconstnode.create(tsetdef(resultdef).setbase,sinttype,false));
 
 
-                      { add a range or a single element? }
-                      if assigned(tsetelementnode(right).right) then
+                      if no_temp then
                         begin
                         begin
-                          { adjust for set base }
-                          tsetelementnode(right).right:=caddnode.create(subn,
-                            ctypeconvnode.create_internal(tsetelementnode(right).right,sinttype),
-                            cordconstnode.create(tsetdef(resultdef).setbase,sinttype,false));
-                          addstatement(newstatement,ccallnode.createintern('fpc_varset_set_range',
-                            ccallparanode.create(cordconstnode.create(resultdef.size,sinttype,false),
-                            ccallparanode.create(tsetelementnode(right).right,
-                            ccallparanode.create(tsetelementnode(right).left,
-                            ccallparanode.create(ctemprefnode.create(temp),
-                            ccallparanode.create(left,nil))))))
-                          );
+                          { add a range or a single element? }
+                          if assigned(tsetelementnode(right).right) then
+                            begin
+                              { adjust for set base }
+                              tsetelementnode(right).right:=caddnode.create(subn,
+                                ctypeconvnode.create_internal(tsetelementnode(right).right,sinttype),
+                                cordconstnode.create(tsetdef(resultdef).setbase,sinttype,false));
+
+                              result:=ccallnode.createintern('fpc_varset_set_range',
+                                ccallparanode.create(cordconstnode.create(resultdef.size,sinttype,false),
+                                ccallparanode.create(tsetelementnode(right).right,
+                                ccallparanode.create(tsetelementnode(right).left,
+                                ccallparanode.create(aktassignmentnode.left.getcopy,
+                                ccallparanode.create(left,nil))))));
+                            end
+                          else
+                            result:=ccallnode.createintern('fpc_varset_set',
+                              ccallparanode.create(cordconstnode.create(resultdef.size,sinttype,false),
+                              ccallparanode.create(ctypeconvnode.create_internal(tsetelementnode(right).left,sinttype),
+                              ccallparanode.create(aktassignmentnode.left.getcopy,
+                              ccallparanode.create(left,nil)))));
+                          include(aktassignmentnode.assignmentnodeflags,anf_assign_done_in_right);
                         end
                         end
                       else
                       else
-                        addstatement(newstatement,ccallnode.createintern('fpc_varset_set',
-                          ccallparanode.create(cordconstnode.create(resultdef.size,sinttype,false),
-                          ccallparanode.create(ctypeconvnode.create_internal(tsetelementnode(right).left,sinttype),
-                          ccallparanode.create(ctemprefnode.create(temp),
-                          ccallparanode.create(left,nil)))))
-                        );
+                        begin
+                          result:=internalstatements(newstatement);
+
+                          { create temp for result }
+                          temp:=ctempcreatenode.create(resultdef,resultdef.size,tt_persistent,true);
+                          addstatement(newstatement,temp);
+
+                          { add a range or a single element? }
+                          if assigned(tsetelementnode(right).right) then
+                            begin
+                              { adjust for set base }
+                              tsetelementnode(right).right:=caddnode.create(subn,
+                                ctypeconvnode.create_internal(tsetelementnode(right).right,sinttype),
+                                cordconstnode.create(tsetdef(resultdef).setbase,sinttype,false));
+                              addstatement(newstatement,ccallnode.createintern('fpc_varset_set_range',
+                                ccallparanode.create(cordconstnode.create(resultdef.size,sinttype,false),
+                                ccallparanode.create(tsetelementnode(right).right,
+                                ccallparanode.create(tsetelementnode(right).left,
+                                ccallparanode.create(ctemprefnode.create(temp),
+                                ccallparanode.create(left,nil))))))
+                              );
+                            end
+                          else
+                            addstatement(newstatement,ccallnode.createintern('fpc_varset_set',
+                              ccallparanode.create(cordconstnode.create(resultdef.size,sinttype,false),
+                              ccallparanode.create(ctypeconvnode.create_internal(tsetelementnode(right).left,sinttype),
+                              ccallparanode.create(ctemprefnode.create(temp),
+                              ccallparanode.create(left,nil)))))
+                            );
+                          { the last statement should return the value as
+                            location and type, this is done be referencing the
+                            temp and converting it first from a persistent temp to
+                            normal temp }
+                          addstatement(newstatement,ctempdeletenode.create_normal_temp(temp));
+                          addstatement(newstatement,ctemprefnode.create(temp));
+                        end;
                       { remove reused parts from original node }
                       { remove reused parts from original node }
                       tsetelementnode(right).right:=nil;
                       tsetelementnode(right).right:=nil;
                       tsetelementnode(right).left:=nil;
                       tsetelementnode(right).left:=nil;
                       left:=nil;
                       left:=nil;
-                      { the last statement should return the value as
-                        location and type, this is done be referencing the
-                        temp and converting it first from a persistent temp to
-                        normal temp }
-                      addstatement(newstatement,ctempdeletenode.create_normal_temp(temp));
-                      addstatement(newstatement,ctemprefnode.create(temp));
                     end
                     end
                   else
                   else
                     call_varset_helper('fpc_varset_add_sets');
                     call_varset_helper('fpc_varset_add_sets');