mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-11-04 05:39:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			359 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			359 lines
		
	
	
		
			12 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]);
 | 
						|
          load:=nextskipping(dealloc,[ait_comment]);
 | 
						|
          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;
 | 
						|
 | 
						|
 | 
						|
      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]);
 | 
						|
                    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) then
 | 
						|
                      begin
 | 
						|
                        removedsomething:=true;
 | 
						|
                        continue;
 | 
						|
                      end;
 | 
						|
                    { todo in peephole optimizer:
 | 
						|
                        alloc regx // not double precision
 | 
						|
                        store regx // not double precision
 | 
						|
                        load  regy or memy
 | 
						|
                        dealloc regx
 | 
						|
                        load regx
 | 
						|
                      -> change into
 | 
						|
                        load regy or memy
 | 
						|
                        swap       // can only handle single precision
 | 
						|
 | 
						|
                      and then
 | 
						|
                        swap
 | 
						|
                        <commutative op>
 | 
						|
                       -> remove swap
 | 
						|
                    }
 | 
						|
                  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.
 |