mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-10-31 10:31:44 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			410 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			410 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| {
 | |
|     Copyright (c) 2010 by Jonas Maebe
 | |
| 
 | |
|     This unit implements the JVM specific class for the register
 | |
|     allocator
 | |
| 
 | |
|     This program is free software; you can redistribute it and/or modify
 | |
|     it under the terms of the GNU General Public License as published by
 | |
|     the Free Software Foundation; either version 2 of the License, or
 | |
|     (at your option) any later version.
 | |
| 
 | |
|     This program is distributed in the hope that it will be useful,
 | |
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|     GNU General Public License for more details.
 | |
| 
 | |
|     You should have received a copy of the GNU General Public License
 | |
|     along with this program; if not, write to the Free Software
 | |
|     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | |
| 
 | |
|  ****************************************************************************}
 | |
| unit rgcpu;
 | |
| 
 | |
| {$i fpcdefs.inc}
 | |
| 
 | |
|   interface
 | |
| 
 | |
|     uses
 | |
|       aasmbase,aasmcpu,aasmtai,aasmdata,
 | |
|       cgbase,cgutils,
 | |
|       cpubase,
 | |
|       rgobj;
 | |
| 
 | |
|     type
 | |
|       tspilltemps = array[tregistertype] of ^Tspill_temp_list;
 | |
| 
 | |
|       { trgcpu }
 | |
| 
 | |
|       trgcpu=class(trgobj)
 | |
|        protected
 | |
|         class procedure do_spill_replace_all(list:TAsmList;instr:taicpu;const spilltemps: tspilltemps);
 | |
|         class procedure remove_dummy_load_stores(list: TAsmList; headertai: tai);
 | |
|        public
 | |
|         { performs the register allocation for *all* register types }
 | |
|         class procedure do_all_register_allocation(list: TAsmList; headertai: tai);
 | |
|       end;
 | |
| 
 | |
| 
 | |
| implementation
 | |
| 
 | |
|     uses
 | |
|       verbose,cutils,
 | |
|       globtype,globals,
 | |
|       cgobj,
 | |
|       tgobj;
 | |
| 
 | |
|     { trgcpu }
 | |
| 
 | |
|     class procedure trgcpu.do_spill_replace_all(list:TAsmList;instr:taicpu;const spilltemps: tspilltemps);
 | |
|       var
 | |
|         l: longint;
 | |
|         reg: tregister;
 | |
|       begin
 | |
|         { jvm instructions never have more than one memory (virtual register)
 | |
|           operand, so there is no danger of superregister conflicts }
 | |
|         for l:=0 to instr.ops-1 do
 | |
|           if instr.oper[l]^.typ=top_reg then
 | |
|             begin
 | |
|               reg:=instr.oper[l]^.reg;
 | |
|               instr.loadref(l,spilltemps[getregtype(reg)]^[getsupreg(reg)]);
 | |
|             end;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     class procedure trgcpu.remove_dummy_load_stores(list: TAsmList; headertai: tai);
 | |
| 
 | |
|       type
 | |
|         taitypeset =  set of taitype;
 | |
| 
 | |
|       function nextskipping(p: tai; const skip: taitypeset): tai;
 | |
|         begin
 | |
|           result:=p;
 | |
|           if not assigned(result) then
 | |
|             exit;
 | |
|           repeat
 | |
|             result:=tai(result.next);
 | |
|           until not assigned(result) or
 | |
|                 not(result.typ in skip);
 | |
|         end;
 | |
| 
 | |
|       function issimpleregstore(p: tai; var reg: tregister; doubleprecisionok: boolean): boolean;
 | |
|         const
 | |
|           simplestoressp = [a_astore,a_fstore,a_istore];
 | |
|           simplestoresdp = [a_dstore,a_lstore];
 | |
|         begin
 | |
|           result:=
 | |
|             assigned(p) and
 | |
|             (p.typ=ait_instruction) and
 | |
|             ((taicpu(p).opcode in simplestoressp) or
 | |
|              (doubleprecisionok and
 | |
|               (taicpu(p).opcode in simplestoresdp))) and
 | |
|             ((reg=NR_NO) or
 | |
|              (taicpu(p).oper[0]^.typ=top_reg) and
 | |
|              (taicpu(p).oper[0]^.reg=reg));
 | |
|           if result and
 | |
|              (reg=NR_NO) then
 | |
|             reg:=taicpu(p).oper[0]^.reg;
 | |
|         end;
 | |
| 
 | |
|       function issimpleregload(p: tai; var reg: tregister; doubleprecisionok: boolean): boolean;
 | |
|         const
 | |
|           simpleloadssp = [a_aload,a_fload,a_iload];
 | |
|           simpleloadsdp = [a_dload,a_lload];
 | |
|         begin
 | |
|           result:=
 | |
|             assigned(p) and
 | |
|             (p.typ=ait_instruction) and
 | |
|             ((taicpu(p).opcode in simpleloadssp) or
 | |
|              (doubleprecisionok and
 | |
|               (taicpu(p).opcode in simpleloadsdp))) and
 | |
|             ((reg=NR_NO) or
 | |
|              (taicpu(p).oper[0]^.typ=top_reg) and
 | |
|              (taicpu(p).oper[0]^.reg=reg));
 | |
|           if result and
 | |
|              (reg=NR_NO) then
 | |
|             reg:=taicpu(p).oper[0]^.reg;
 | |
|         end;
 | |
| 
 | |
|       function isregallocoftyp(p: tai; typ: TRegAllocType;var reg: tregister): boolean;
 | |
|         begin
 | |
|           result:=
 | |
|             assigned(p) and
 | |
|             (p.typ=ait_regalloc) and
 | |
|             (tai_regalloc(p).ratype=typ);
 | |
|           if result then
 | |
|             if reg=NR_NO then
 | |
|               reg:=tai_regalloc(p).reg
 | |
|             else
 | |
|               result:=tai_regalloc(p).reg=reg;
 | |
|         end;
 | |
| 
 | |
|       function regininstruction(p: tai; reg: tregister): boolean;
 | |
|         var
 | |
|           sr: tsuperregister;
 | |
|           i: longint;
 | |
|         begin
 | |
|           result:=false;
 | |
|           if p.typ<>ait_instruction then
 | |
|             exit;
 | |
|           sr:=getsupreg(reg);
 | |
|           for i:=0 to taicpu(p).ops-1 do
 | |
|             case taicpu(p).oper[0]^.typ of
 | |
|               top_reg:
 | |
|                 if (getsupreg(taicpu(p).oper[0]^.reg)=sr) then
 | |
|                   exit(true);
 | |
|               top_ref:
 | |
|                 begin
 | |
|                   if (getsupreg(taicpu(p).oper[0]^.ref^.base)=sr) then
 | |
|                     exit(true);
 | |
|                   if (getsupreg(taicpu(p).oper[0]^.ref^.index)=sr) then
 | |
|                     exit(true);
 | |
|                   if (getsupreg(taicpu(p).oper[0]^.ref^.indexbase)=sr) then
 | |
|                     exit(true);
 | |
|                   if (getsupreg(taicpu(p).oper[0]^.ref^.indexbase)=sr) then
 | |
|                     exit(true);
 | |
|                 end;
 | |
|             end;
 | |
|         end;
 | |
| 
 | |
|       function try_remove_store_dealloc_load(var p: tai): boolean;
 | |
|         var
 | |
|           dealloc,
 | |
|           load: tai;
 | |
|           reg: tregister;
 | |
|         begin
 | |
|           result:=false;
 | |
|           { check for:
 | |
|               store regx
 | |
|               dealloc regx
 | |
|               load regx
 | |
|             and remove. We don't have to check that the load/store
 | |
|             types match, because they have to for this to be
 | |
|             valid JVM code }
 | |
|           dealloc:=nextskipping(p,[ait_comment,ait_tempalloc]);
 | |
|           load:=nextskipping(dealloc,[ait_comment,ait_tempalloc]);
 | |
|           reg:=NR_NO;
 | |
|           if issimpleregstore(p,reg,true) and
 | |
|              isregallocoftyp(dealloc,ra_dealloc,reg) and
 | |
|              issimpleregload(load,reg,true) then
 | |
|             begin
 | |
|               { remove the whole sequence: the store }
 | |
|               list.remove(p);
 | |
|               p.free;
 | |
|               p:=Tai(load.next);
 | |
|               { the load }
 | |
|               list.remove(load);
 | |
|               load.free;
 | |
| 
 | |
|               result:=true;
 | |
|             end;
 | |
|         end;
 | |
| 
 | |
| 
 | |
|      function try_swap_store_x_load(var p: tai): boolean;
 | |
|        var
 | |
|          insertpos,
 | |
|          storex,
 | |
|          deallocy,
 | |
|          loady,
 | |
|          deallocx,
 | |
|          loadx: tai;
 | |
|          swapxy: taicpu;
 | |
|          regx, regy: tregister;
 | |
|        begin
 | |
|          result:=false;
 | |
|          { check for:
 | |
|              alloc regx (optional)
 | |
|              store regx (p)
 | |
|              dealloc regy
 | |
|              load regy
 | |
|              dealloc regx
 | |
|              load regx
 | |
|            and change to
 | |
|              dealloc regy
 | |
|              load regy
 | |
|              swap
 | |
|              alloc regx (if it existed)
 | |
|              store regx
 | |
|              dealloc regx
 | |
|              load  regx
 | |
| 
 | |
|            This will create opportunities to remove the store/load regx
 | |
|            (and possibly also for regy)
 | |
|          }
 | |
|          regx:=NR_NO;
 | |
|          regy:=NR_NO;
 | |
|          if not issimpleregstore(p,regx,false) then
 | |
|            exit;
 | |
|          storex:=p;
 | |
|          deallocy:=nextskipping(storex,[ait_comment,ait_tempalloc]);
 | |
|          loady:=nextskipping(deallocy,[ait_comment,ait_tempalloc]);
 | |
|          deallocx:=nextskipping(loady,[ait_comment,ait_tempalloc]);
 | |
|          loadx:=nextskipping(deallocx,[ait_comment,ait_tempalloc]);
 | |
|          if not assigned(loadx) then
 | |
|            exit;
 | |
|          if not issimpleregload(loady,regy,false) then
 | |
|            exit;
 | |
|          if not issimpleregload(loadx,regx,false) then
 | |
|            exit;
 | |
|          if not isregallocoftyp(deallocy,ra_dealloc,regy) then
 | |
|            exit;
 | |
|          if not isregallocoftyp(deallocx,ra_dealloc,regx) then
 | |
|            exit;
 | |
|          insertpos:=tai(p.previous);
 | |
|          if not assigned(insertpos) or
 | |
|             not isregallocoftyp(insertpos,ra_alloc,regx) then
 | |
|            insertpos:=storex;
 | |
|          list.remove(deallocy);
 | |
|          list.insertbefore(deallocy,insertpos);
 | |
|          list.remove(loady);
 | |
|          list.insertbefore(loady,insertpos);
 | |
|          swapxy:=taicpu.op_none(a_swap);
 | |
|          swapxy.fileinfo:=taicpu(loady).fileinfo;
 | |
|          list.insertbefore(swapxy,insertpos);
 | |
|          result:=true;
 | |
|        end;
 | |
| 
 | |
| 
 | |
|       var
 | |
|         p,next,nextnext: tai;
 | |
|         reg: tregister;
 | |
|         removedsomething: boolean;
 | |
|       begin
 | |
|         repeat
 | |
|           removedsomething:=false;
 | |
|           p:=headertai;
 | |
|           while assigned(p) do
 | |
|             begin
 | |
|               case p.typ of
 | |
|                 ait_regalloc:
 | |
|                   begin
 | |
|                     reg:=NR_NO;
 | |
|                     next:=nextskipping(p,[ait_comment,ait_tempalloc]);
 | |
|                     nextnext:=nextskipping(next,[ait_comment,ait_regalloc]);
 | |
|                     if assigned(nextnext) then
 | |
|                       begin
 | |
|                         { remove
 | |
|                             alloc reg
 | |
|                             dealloc reg
 | |
| 
 | |
|                           (can appear after optimisations, necessary to prevent
 | |
|                            useless stack slot allocations) }
 | |
|                         if isregallocoftyp(p,ra_alloc,reg) and
 | |
|                            isregallocoftyp(next,ra_dealloc,reg) and
 | |
|                            not regininstruction(nextnext,reg) then
 | |
|                           begin
 | |
|                             list.remove(p);
 | |
|                             p.free;
 | |
|                             p:=tai(next.next);
 | |
|                             list.remove(next);
 | |
|                             next.free;
 | |
|                             removedsomething:=true;
 | |
|                             continue;
 | |
|                           end;
 | |
|                       end;
 | |
|                   end;
 | |
|                 ait_instruction:
 | |
|                   begin
 | |
|                     if try_remove_store_dealloc_load(p) or
 | |
|                        try_swap_store_x_load(p) then
 | |
|                       begin
 | |
|                         removedsomething:=true;
 | |
|                         continue;
 | |
|                       end;
 | |
|                   end;
 | |
|               end;
 | |
|               p:=tai(p.next);
 | |
|             end;
 | |
|         until not removedsomething;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     class procedure trgcpu.do_all_register_allocation(list: TAsmList; headertai: tai);
 | |
|       var
 | |
|         spill_temps : tspilltemps;
 | |
|         templist : TAsmList;
 | |
|         intrg,
 | |
|         fprg     : trgcpu;
 | |
|         p,q      : tai;
 | |
|         size     : longint;
 | |
|       begin
 | |
|         { Since there are no actual registers, we simply spill everything. We
 | |
|           use tt_regallocator temps, which are not used by the temp allocator
 | |
|           during code generation, so that we cannot accidentally overwrite
 | |
|           any temporary values }
 | |
| 
 | |
|         { get references to all register allocators }
 | |
|         intrg:=trgcpu(cg.rg[R_INTREGISTER]);
 | |
|         fprg:=trgcpu(cg.rg[R_FPUREGISTER]);
 | |
|         { determine the live ranges of all registers }
 | |
|         intrg.insert_regalloc_info_all(list);
 | |
|         fprg.insert_regalloc_info_all(list);
 | |
|         { Don't do the actual allocation when -sr is passed }
 | |
|         if (cs_no_regalloc in current_settings.globalswitches) then
 | |
|           exit;
 | |
|         { remove some simple useless store/load sequences }
 | |
|         remove_dummy_load_stores(list,headertai);
 | |
|         { allocate room to store the virtual register -> temp mapping }
 | |
|         spill_temps[R_INTREGISTER]:=allocmem(sizeof(treference)*intrg.maxreg);
 | |
|         spill_temps[R_FPUREGISTER]:=allocmem(sizeof(treference)*fprg.maxreg);
 | |
|         { List to insert temp allocations into }
 | |
|         templist:=TAsmList.create;
 | |
|         { allocate/replace all registers }
 | |
|         p:=headertai;
 | |
|         while assigned(p) do
 | |
|           begin
 | |
|             case p.typ of
 | |
|               ait_regalloc:
 | |
|                 with Tai_regalloc(p) do
 | |
|                   begin
 | |
|                     case getregtype(reg) of
 | |
|                       R_INTREGISTER:
 | |
|                         if getsubreg(reg)=R_SUBD then
 | |
|                           size:=4
 | |
|                         else
 | |
|                           size:=8;
 | |
|                       R_ADDRESSREGISTER:
 | |
|                         size:=4;
 | |
|                       R_FPUREGISTER:
 | |
|                         if getsubreg(reg)=R_SUBFS then
 | |
|                           size:=4
 | |
|                         else
 | |
|                           size:=8;
 | |
|                       else
 | |
|                         internalerror(2010122912);
 | |
|                     end;
 | |
|                     case ratype of
 | |
|                       ra_alloc :
 | |
|                         tg.gettemp(templist,
 | |
|                                    size,1,
 | |
|                                    tt_regallocator,spill_temps[getregtype(reg)]^[getsupreg(reg)]);
 | |
|                       ra_dealloc :
 | |
|                         begin
 | |
|                           tg.ungettemp(templist,spill_temps[getregtype(reg)]^[getsupreg(reg)]);
 | |
|                           { don't invalidate the temp reference, may still be used one instruction
 | |
|                             later }
 | |
|                         end;
 | |
|                     end;
 | |
|                     { insert the tempallocation/free at the right place }
 | |
|                     list.insertlistbefore(p,templist);
 | |
|                     { remove the register allocation info for the register
 | |
|                       (p.previous is valid because we just inserted the temp
 | |
|                        allocation/free before p) }
 | |
|                     q:=Tai(p.previous);
 | |
|                     list.remove(p);
 | |
|                     p.free;
 | |
|                     p:=q;
 | |
|                   end;
 | |
|               ait_instruction:
 | |
|                 do_spill_replace_all(list,taicpu(p),spill_temps);
 | |
|             end;
 | |
|             p:=Tai(p.next);
 | |
|           end;
 | |
|         freemem(spill_temps[R_INTREGISTER]);
 | |
|         freemem(spill_temps[R_FPUREGISTER]);
 | |
|         templist.free;
 | |
|       end;
 | |
| 
 | |
| end.
 | 
