{ 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 -> 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.