{ Copyright (c) 2013 by Jonas Maebe, member of the Free Pascal development team This unit implements the LLVM-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 rgllvm; {$i fpcdefs.inc} interface uses aasmcpu,aasmsym,aasmtai,aasmdata, symtype, cgbase,cgutils, cpubase,llvmbase, rgobj; type { trgllvm } trgllvm=class(trgobj) constructor create(Aregtype: Tregistertype; Adefaultsub: Tsubregister; const Ausable: array of tsuperregister; Afirst_imaginary: Tsuperregister; Apreserved_by_proc: Tcpuregisterset); reintroduce; procedure do_register_allocation(list: TAsmList; headertai: tai); override; procedure do_spill_read(list: TAsmList; pos: tai; const spilltemp: treference; tempreg: tregister; orgsupreg: tsuperregister); override; procedure do_spill_written(list: TAsmList; pos: tai; const spilltemp: treference; tempreg: tregister; orgsupreg: tsuperregister); override; protected function instr_get_oper_spilling_info(var spregs: tspillregsinfo; const r: tsuperregisterset; instr: tai_cpu_abstract_sym; opidx: longint): boolean; override; procedure substitute_spilled_registers(const spregs: tspillregsinfo; instr: tai_cpu_abstract_sym; opidx: longint); override; procedure determine_spill_registers(list: TasmList; headertai: tai); override; procedure get_spill_temp(list:TAsmlist;spill_temps: Tspill_temp_list; supreg: tsuperregister);override; strict protected type tregwrites = (rw_none, rw_one, rw_multiple); pwrittenregs = ^twrittenregs; twrittenregs = bitpacked array[tsuperregister] of tregwrites; var spillcounter: longint; writtenregs: pwrittenregs; end; implementation uses verbose,cutils, globtype,globals, symdef, aasmllvm, tgobj; { trgllvm } constructor trgllvm.create(Aregtype: Tregistertype; Adefaultsub: Tsubregister; const Ausable: array of tsuperregister; Afirst_imaginary: Tsuperregister; Apreserved_by_proc: Tcpuregisterset); begin inherited; { tell the generic register allocator to generate SSA spilling code } ssa_safe:=true; { all registers are "usable" for us; we only care about SSA form. This prevents the register allocator from trying to spill every single register (because our "usable registers" array contains just one, dummy, register) } usable_registers_cnt:=high(usable_registers_cnt); end; procedure trgllvm.do_register_allocation(list: TAsmList; headertai: tai); begin { these are SSA by design, they're only assigned by alloca instructions } if regtype=R_TEMPREGISTER then exit; inherited; end; procedure trgllvm.do_spill_read(list: TAsmList; pos: tai; const spilltemp: treference; tempreg: tregister; orgsupreg: tsuperregister); var ins: taillvm; def: tdef; begin def:=tdef(reginfo[orgsupreg].def); if not assigned(def) then internalerror(2013110803); ins:=taillvm.op_reg_size_ref(la_load,tempreg,cpointerdef.getreusable(def),spilltemp); list.insertafter(ins,pos); {$ifdef DEBUG_SPILLING} list.Insertbefore(tai_comment.Create(strpnew('Spilling: Spill Read')),ins); {$endif} end; procedure trgllvm.do_spill_written(list: TAsmList; pos: tai; const spilltemp: treference; tempreg: tregister; orgsupreg: tsuperregister); var ins: taillvm; def: tdef; begin def:=tdef(reginfo[orgsupreg].def); if not assigned(def) then internalerror(2013110802); ins:=taillvm.op_size_reg_size_ref(la_store,def,tempreg,cpointerdef.getreusable(def),spilltemp); list.insertafter(ins,pos); {$ifdef DEBUG_SPILLING} list.Insertbefore(tai_comment.Create(strpnew('Spilling: Spill Write')),ins); {$endif} end; function trgllvm.instr_get_oper_spilling_info(var spregs: tspillregsinfo; const r: tsuperregisterset; instr: tai_cpu_abstract_sym; opidx: longint): boolean; var paracnt: longint; callpara: pllvmcallpara; begin result:=false; with instr.oper[opidx]^ do begin case typ of top_para: begin for paracnt:=0 to paras.count-1 do begin callpara:=pllvmcallpara(paras[paracnt]); if (callpara^.val.typ=top_reg) and (getregtype(callpara^.val.register)=regtype) then begin result:=addreginfo(spregs,r,callpara^.val.register,operand_read) or result; break end; end; end; else result:=inherited; end; end; end; procedure trgllvm.substitute_spilled_registers(const spregs: tspillregsinfo; instr: tai_cpu_abstract_sym; opidx: longint); var paracnt: longint; callpara: pllvmcallpara; begin with instr.oper[opidx]^ do case typ of top_para: begin for paracnt:=0 to paras.count-1 do begin callpara:=pllvmcallpara(paras[paracnt]); if (callpara^.val.typ=top_reg) and (getregtype(callpara^.val.register)=regtype) then try_replace_reg(spregs, callpara^.val.register,true); end; end; else inherited; end; end; procedure trgllvm.determine_spill_registers(list: TasmList; headertai: tai); var hp: tai; reg: tregister; sr: tsuperregister; i: longint; begin spillednodes.clear; { there should be only one round of spilling per register type, we shouldn't generate multiple writes to a single register here } if spillcounter<>0 then exit; { registers must be in SSA form -> determine all registers that are written to more than once } hp:=headertai; { 2 bits per superregister, rounded up to a byte } writtenregs:=allocmem((maxreg*bitsizeof(twrittenregs[low(tsuperregister)])+7) shr 3); while assigned(hp) do begin case hp.typ of ait_llvmins: begin for i:=0 to taillvm(hp).ops-1 do if (taillvm(hp).oper[i]^.typ=top_reg) and (getregtype(taillvm(hp).oper[i]^.reg)=regtype) and (taillvm(hp).spilling_get_operation_type(i)=operand_write) then begin reg:=taillvm(hp).oper[i]^.reg; sr:=getsupreg(reg); if writtenregs^[sr]ait_llvmins then internalerror(2013110701); { determine type of register so we can allocate a temp of the right type } def:=nil; for i:=0 to taillvm(supstart).ops-1 do begin case taillvm(supstart).oper[i]^.typ of top_reg: if (getregtype(taillvm(supstart).oper[i]^.reg)=regtype) and (getsupreg(taillvm(supstart).oper[i]^.reg)=supreg) then begin def:=taillvm(supstart).spilling_get_reg_type(i); break end; top_para: begin for paracnt:=0 to taillvm(supstart).oper[i]^.paras.count-1 do begin callpara:=pllvmcallpara(taillvm(supstart).oper[i]^.paras[paracnt]); if (callpara^.val.typ=top_reg) and (getregtype(callpara^.val.register)=regtype) and (getsupreg(callpara^.val.register)=supreg) then begin def:=callpara^.def; break end; end; end; else ; end; end; if not assigned(def) then internalerror(2013110702); tg.gethltemp(list,def,def.size,tt_noreuse,spill_temps[supreg]); { record for use in spill instructions } reginfo[supreg].def:=def; end; end.