瀏覽代碼

+ "reference" temps that sort of implement pointer-style functionality for
platforms that don't support pointers (by make a copy of all registers
part of a reference)

git-svn-id: branches/jvmbackend@18377 -

Jonas Maebe 14 年之前
父節點
當前提交
a2a6b2fd1d
共有 5 個文件被更改,包括 191 次插入20 次删除
  1. 9 0
      compiler/hlcgobj.pas
  2. 1 1
      compiler/htypechk.pas
  3. 60 0
      compiler/jvm/hlcgcpu.pas
  4. 79 3
      compiler/nbas.pas
  5. 42 16
      compiler/ncgbas.pas

+ 9 - 0
compiler/hlcgobj.pas

@@ -412,6 +412,15 @@ unit hlcgobj;
           The default implementation issues a jump instruction to the external name. }
 //          procedure g_external_wrapper(list : TAsmList; procdef: tprocdef; const externalname: string); virtual;
 
+          { create "safe copy" of a tlocation that can be used later: all
+            registers used in the tlocation are copied to new ones, so that
+            even if the original ones change, things stay the same (except if
+            the original location was already a register, then the register is
+            kept). Must only be used on lvalue locations.
+            It's intended as some kind of replacement for a_loadaddr_ref_reg()
+            for targets without pointers. }
+          procedure g_reference_loc(list: TAsmList; def: tdef; const fromloc: tlocation; out toloc: tlocation); virtual; abstract;
+
 
           { routines migrated from ncgutil }
 

+ 1 - 1
compiler/htypechk.pas

@@ -1181,7 +1181,7 @@ implementation
            case hp.nodetype of
              temprefn :
                begin
-                 valid_for_assign := true;
+                 valid_for_assign := not(ti_readonly in ttemprefnode(hp).tempinfo^.flags);
                  exit;
                end;
              derefn :

+ 60 - 0
compiler/jvm/hlcgcpu.pas

@@ -111,6 +111,8 @@ uses
       procedure a_op_ref_stack(list : TAsmList;op: topcg; size: tdef;const ref: treference);
       procedure a_op_loc_stack(list : TAsmList;op: topcg; size: tdef;const loc: tlocation);
 
+      procedure g_reference_loc(list: TAsmList; def: tdef; const fromloc: tlocation; out toloc: tlocation); override;
+
       { this routine expects that all values are already massaged into the
         required form (sign bits xor'ed for gt/lt comparisons for OS_32/OS_64,
         see http://stackoverflow.com/questions/4068973/c-performing-signed-comparison-in-unsigned-variables-without-casting ) }
@@ -461,6 +463,64 @@ implementation
       end;
     end;
 
+
+  procedure thlcgjvm.g_reference_loc(list: TAsmList; def: tdef; const fromloc: tlocation; out toloc: tlocation);
+
+    procedure handle_reg_move(regsize: tdef; const fromreg: tregister; out toreg: tregister; regtyp: tregistertype);
+      begin
+        case regtyp of
+          R_INTREGISTER:
+            toreg:=getintregister(list,regsize);
+          R_ADDRESSREGISTER:
+            toreg:=getaddressregister(list,regsize);
+          R_FPUREGISTER:
+            toreg:=getfpuregister(list,regsize);
+        end;
+        a_load_reg_reg(list,regsize,regsize,fromreg,toreg);
+      end;
+
+    begin
+      toloc:=fromloc;
+      case fromloc.loc of
+        { volatile location, can't get a permanent reference }
+        LOC_REGISTER,
+        LOC_FPUREGISTER:
+          internalerror(2011031406);
+        LOC_CONSTANT:
+          { finished }
+          ;
+        LOC_CREGISTER:
+          handle_reg_move(def,fromloc.reference.index,toloc.reference.index,R_INTREGISTER);
+        LOC_CFPUREGISTER:
+          handle_reg_move(def,fromloc.reference.index,toloc.reference.index,R_FPUREGISTER);
+        { although LOC_CREFERENCE cannot be an lvalue, we may want to take a
+          reference to such a location for multiple reading }
+        LOC_CREFERENCE,
+        LOC_REFERENCE:
+          begin
+            if (fromloc.reference.base<>NR_NO) and
+               (fromloc.reference.base<>current_procinfo.framepointer) and
+               (fromloc.reference.base<>NR_STACK_POINTER_REG) then
+              handle_reg_move(java_jlobject,fromloc.reference.base,toloc.reference.base,R_ADDRESSREGISTER);
+            case fromloc.reference.arrayreftype of
+              art_indexreg:
+                begin
+                  { all array indices in Java are 32 bit ints }
+                  handle_reg_move(s32inttype,fromloc.reference.index,toloc.reference.index,R_INTREGISTER);
+                end;
+              art_indexref:
+                begin
+                  if (fromloc.reference.indexbase<>NR_NO) and
+                     (fromloc.reference.indexbase<>NR_STACK_POINTER_REG) then
+                    handle_reg_move(s32inttype,fromloc.reference.indexbase,toloc.reference.indexbase,R_ADDRESSREGISTER);
+                end;
+            end;
+          end;
+        else
+          internalerror(2011031407);
+      end;
+    end;
+
     procedure thlcgjvm.a_cmp_stack_label(list: TAsmlist; size: tdef; cmp_op: topcmp; lab: tasmlabel);
       const
         opcmp2icmp: array[topcmp] of tasmop = (A_None,

+ 79 - 3
compiler/nbas.pas

@@ -94,12 +94,57 @@ interface
 
        ttempcreatenode = class;
 
-       ttempinfoflag = (ti_may_be_in_reg,ti_valid,ti_nextref_set_hookoncopy_nil,
-                        ti_addr_taken,ti_executeinitialisation);
+       ttempinfoflag = (
+         { temp can be kept in a register as far as the original creator is
+          concerned }
+         ti_may_be_in_reg,
+         { the ttempcreatenode has been process and the temp's location is
+           valid (-> the ttempdeletenode has not yet been processed, or
+           in case it's a "create_to_normal()" one, the final ttemprefnode
+           has not yet been processed) }
+         ti_valid,
+         { when performing a getcopy of a nodetree, we have to hook up the
+           copies of ttemprefnodes and ttempdestroynode to the copied
+           ttempinfo. this is done by setting hookoncopy in the original
+           ttempinfo to point to the new one. if the temp is deleted via a
+           regular ttempdeletenode, the hookoncopy is simply set to nil once
+           it's processed. otherwise, it sets the ti_nextref_set_hookoncopy_nil
+           and after processing the final ttemprefnode, hookoncopy is set to nil
+         }
+         ti_nextref_set_hookoncopy_nil,
+         { the address of this temp is taken (-> cannot be kept in a register,
+           even if the creator didn't mind)
+         }
+         ti_addr_taken,
+         { temps can get an extra node tree that contains the value to which
+           they should be initialised when they are created. this initialisation
+           has to be performed right before the first reference to the temp.
+           this flag indicates that the ttempcreatenode has been
+           processed by pass_generate_code, but that the first ttemprefnode
+           hasn't yet and hence will have to perform the initialisation
+         }
+         ti_executeinitialisation,
+         { in case an expression like "inc(x[func()],1)" is translated into
+           a regular addition, you have to create a temp to hold the address
+           representing x[func()], since otherwise func() will be called twice
+           and that can spell trouble in case it has side effects. on platforms
+           without pointers, we cannot just take the address though. this flag
+           has to be combined with ti_executeinitialisation above and will,
+           rather than loading the value at the calculated location and store
+           it in the temp, keep a copy of the calculated location if possible
+           and required (not possible for regvars, because SSA may change their
+           register, but not required for them either since calculating their
+           location has no side-effects
+         }
+         ti_reference,
+         { this temp only allows reading (makes it possible to safely use as
+           reference under more circumstances)
+         }
+         ti_readonly);
        ttempinfoflags = set of ttempinfoflag;
 
      const
-       tempinfostoreflags = [ti_may_be_in_reg,ti_addr_taken];
+       tempinfostoreflags = [ti_may_be_in_reg,ti_addr_taken,ti_reference,ti_readonly];
 
      type
        { to allow access to the location by temp references even after the temp has }
@@ -136,6 +181,7 @@ interface
           constructor create(_typedef: tdef; _size: tcgint; _temptype: ttemptype;allowreg:boolean); virtual;
           constructor create_withnode(_typedef: tdef; _size: tcgint; _temptype: ttemptype; allowreg:boolean; withnode: tnode); virtual;
           constructor create_value(_typedef:tdef; _size: tcgint; _temptype: ttemptype;allowreg:boolean; templvalue: tnode);
+          constructor create_reference(_typedef:tdef; _size: tcgint; _temptype: ttemptype;allowreg:boolean; templvalue: tnode; readonly: boolean);
           constructor ppuload(t:tnodetype;ppufile:tcompilerppufile);override;
           procedure ppuwrite(ppufile:tcompilerppufile);override;
           procedure buildderefimpl;override;
@@ -209,6 +255,9 @@ interface
        function  laststatement(block:tblocknode):tstatementnode;
        procedure addstatement(var laststatement:tstatementnode;n:tnode);
 
+       { if the complexity of n is "high", creates a reference temp to n's
+         location and replace n with a ttemprefnode referring to that location }
+       function maybereplacewithtempref(var n: tnode; size: ASizeInt; readonly: boolean): ttempcreatenode;
 
 implementation
 
@@ -251,6 +300,20 @@ implementation
       end;
 
 
+    function maybereplacewithtempref(var n: tnode; size: ASizeInt; readonly: boolean): ttempcreatenode;
+      begin
+        result:=nil;
+        if node_complexity(n) > 4 then
+          begin
+            result:=ctempcreatenode.create_reference(n.resultdef,size,tt_persistent,true,n,readonly);
+            typecheckpass(tnode(result));
+            n:=ctemprefnode.create(result);
+            typecheckpass(n);
+          end;
+      end;
+
+
+
 {*****************************************************************************
                              TFIRSTNOTHING
 *****************************************************************************}
@@ -734,6 +797,19 @@ implementation
       end;
 
 
+     constructor ttempcreatenode.create_reference(_typedef: tdef; _size: tcgint; _temptype: ttemptype; allowreg: boolean; templvalue: tnode; readonly: boolean);
+      begin
+        // store in ppuwrite
+        self.create(_typedef,_size,_temptype,allowreg);
+        ftemplvalue:=templvalue;
+        // no assignment node, just the tempvalue
+        tempinfo^.tempinitcode:=ftemplvalue;
+        include(tempinfo^.flags,ti_reference);
+        if readonly then
+          include(tempinfo^.flags,ti_readonly);
+      end;
+
+
     function ttempcreatenode.dogetcopy: tnode;
       var
         n: ttempcreatenode;

+ 42 - 16
compiler/ncgbas.pas

@@ -395,23 +395,28 @@ interface
         if (ti_valid in tempinfo^.flags) then
           internalerror(200108222);
 
-        { get a (persistent) temp }
-        if is_managed_type(tempinfo^.typedef) then
+        { in case of ti_reference, the location will be initialised using the
+          location of the tempinitnode once the first temprefnode is processed }
+        if not(ti_reference in tempinfo^.flags) then
           begin
-            location_reset_ref(tempinfo^.location,LOC_REFERENCE,def_cgsize(tempinfo^.typedef),0);
-            tg.GetTempTyped(current_asmdata.CurrAsmList,tempinfo^.typedef,tempinfo^.temptype,tempinfo^.location.reference);
-            { the temp could have been used previously either because the memory location was reused or
-              because we're in a loop }
-            hlcg.g_finalize(current_asmdata.CurrAsmList,tempinfo^.typedef,tempinfo^.location.reference);
-          end
-        else if (ti_may_be_in_reg in tempinfo^.flags) then
-          begin
-            location_allocate_register(current_asmdata.CurrAsmList,tempinfo^.location,tempinfo^.typedef,tempinfo^.temptype = tt_persistent);
-          end
-        else
-          begin
-            location_reset_ref(tempinfo^.location,LOC_REFERENCE,def_cgsize(tempinfo^.typedef),0);
-            tg.gethltemp(current_asmdata.CurrAsmList,tempinfo^.typedef,size,tempinfo^.temptype,tempinfo^.location.reference);
+            { get a (persistent) temp }
+            if is_managed_type(tempinfo^.typedef) then
+              begin
+                location_reset_ref(tempinfo^.location,LOC_REFERENCE,def_cgsize(tempinfo^.typedef),0);
+                tg.GetTempTyped(current_asmdata.CurrAsmList,tempinfo^.typedef,tempinfo^.temptype,tempinfo^.location.reference);
+                { the temp could have been used previously either because the memory location was reused or
+                  because we're in a loop }
+                hlcg.g_finalize(current_asmdata.CurrAsmList,tempinfo^.typedef,tempinfo^.location.reference);
+              end
+            else if (ti_may_be_in_reg in tempinfo^.flags) then
+              begin
+                location_allocate_register(current_asmdata.CurrAsmList,tempinfo^.location,tempinfo^.typedef,tempinfo^.temptype = tt_persistent);
+              end
+            else
+              begin
+                location_reset_ref(tempinfo^.location,LOC_REFERENCE,def_cgsize(tempinfo^.typedef),0);
+                tg.gethltemp(current_asmdata.CurrAsmList,tempinfo^.typedef,size,tempinfo^.temptype,tempinfo^.location.reference);
+              end;
           end;
         include(tempinfo^.flags,ti_valid);
         if assigned(tempinfo^.tempinitcode) then
@@ -430,6 +435,27 @@ interface
             { avoid recursion }
             exclude(tempinfo^.flags, ti_executeinitialisation);
             secondpass(tempinfo^.tempinitcode);
+            if (ti_reference in tempinfo^.flags) then
+              begin
+                case tempinfo^.tempinitcode.location.loc of
+                  LOC_CREGISTER,
+                  LOC_CFPUREGISTER,
+                  LOC_CMMREGISTER,
+                  LOC_CSUBSETREG:
+                    begin
+                      { although it's ok if we need this value multiple times
+                        for reading, it's not in case of writing (because the
+                        register could change due to SSA -> storing to the saved
+                        register afterwards would be wrong). }
+                      if not(ti_readonly in tempinfo^.flags) then
+                        internalerror(2011031407);
+                    end;
+                  { in case reference contains CREGISTERS, that doesn't matter:
+                    we want to write to the location indicated by the current
+                    value of those registers, and we can save those values }
+                end;
+                hlcg.g_reference_loc(current_asmdata.CurrAsmList,tempinfo^.typedef,tempinfo^.tempinitcode.location,tempinfo^.location);
+              end;
           end;
         { check if the temp is valid }
         if not(ti_valid in tempinfo^.flags) then