1 {
2     Copyright (c) 2013 by Jonas Maebe, member of the Free Pascal development
3     team
4 
5     This unit implements the LLVM-specific class for the register
6     allocator
7 
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22  ****************************************************************************}
23 unit rgllvm;
24 
25 {$i fpcdefs.inc}
26 
27   interface
28 
29     uses
30       aasmcpu,aasmsym,aasmtai,aasmdata,
31       symtype,
32       cgbase,cgutils,
33       cpubase,llvmbase,
34       rgobj;
35 
36     type
37       { trgllvm }
38       trgllvm=class(trgobj)
39         constructor create(Aregtype: Tregistertype; Adefaultsub: Tsubregister; const Ausable: array of tsuperregister; Afirst_imaginary: Tsuperregister; Apreserved_by_proc: Tcpuregisterset); reintroduce;
40         procedure do_register_allocation(list: TAsmList; headertai: tai); override;
41         procedure do_spill_read(list: TAsmList; pos: tai; const spilltemp: treference; tempreg: tregister; orgsupreg: tsuperregister); override;
42         procedure do_spill_written(list: TAsmList; pos: tai; const spilltemp: treference; tempreg: tregister; orgsupreg: tsuperregister); override;
43        protected
instr_get_oper_spilling_infonull44         function instr_get_oper_spilling_info(var regs: tspillregsinfo; const r: tsuperregisterset; instr: tai_cpu_abstract_sym; opidx: longint): boolean; override;
45         procedure substitute_spilled_registers(const regs: tspillregsinfo; instr: tai_cpu_abstract_sym; opidx: longint); override;
46         procedure determine_spill_registers(list: TasmList; headertai: tai); override;
47         procedure get_spill_temp(list:TAsmlist;spill_temps: Pspill_temp_list; supreg: tsuperregister);override;
48        strict protected
49        type
50          tregwrites = (rw_none, rw_one, rw_multiple);
51          pwrittenregs = ^twrittenregs;
52          twrittenregs = bitpacked array[tsuperregister] of tregwrites;
53        var
54         spillcounter: longint;
55         writtenregs: pwrittenregs;
56       end;
57 
58 
59 implementation
60 
61     uses
62       verbose,cutils,
63       globtype,globals,
64       symdef,
65       aasmllvm,
66       tgobj;
67 
68     { trgllvm }
69 
70      constructor trgllvm.create(Aregtype: Tregistertype; Adefaultsub: Tsubregister; const Ausable: array of tsuperregister; Afirst_imaginary: Tsuperregister; Apreserved_by_proc: Tcpuregisterset);
71        begin
72          inherited;
73          { tell the generic register allocator to generate SSA spilling code }
74          ssa_safe:=true;
75        end;
76 
77      procedure trgllvm.do_register_allocation(list: TAsmList; headertai: tai);
78       begin
79         { these are SSA by design, they're only assigned by alloca
80           instructions }
81         if regtype=R_TEMPREGISTER then
82           exit;
83         inherited;
84       end;
85 
86 
87     procedure trgllvm.do_spill_read(list: TAsmList; pos: tai; const spilltemp: treference; tempreg: tregister; orgsupreg: tsuperregister);
88       var
89         ins: taillvm;
90         def: tdef;
91       begin
92         def:=tdef(reginfo[orgsupreg].def);
93         if not assigned(def) then
94           internalerror(2013110803);
95         ins:=taillvm.op_reg_size_ref(la_load,tempreg,cpointerdef.getreusable(def),spilltemp);
96         list.insertafter(ins,pos);
97         {$ifdef DEBUG_SPILLING}
98         list.Insertbefore(tai_comment.Create(strpnew('Spilling: Spill Read')),ins);
99         {$endif}
100       end;
101 
102 
103     procedure trgllvm.do_spill_written(list: TAsmList; pos: tai; const spilltemp: treference; tempreg: tregister; orgsupreg: tsuperregister);
104       var
105         ins: taillvm;
106         def: tdef;
107       begin
108         def:=tdef(reginfo[orgsupreg].def);
109         if not assigned(def) then
110           internalerror(2013110802);
111         ins:=taillvm.op_size_reg_size_ref(la_store,def,tempreg,cpointerdef.getreusable(def),spilltemp);
112         list.insertafter(ins,pos);
113         {$ifdef DEBUG_SPILLING}
114         list.Insertbefore(tai_comment.Create(strpnew('Spilling: Spill Write')),ins);
115         {$endif}
116       end;
117 
118 
trgllvm.instr_get_oper_spilling_infonull119     function trgllvm.instr_get_oper_spilling_info(var regs: tspillregsinfo; const r: tsuperregisterset; instr: tai_cpu_abstract_sym; opidx: longint): boolean;
120       var
121         i, paracnt: longint;
122         callpara: pllvmcallpara;
123       begin
124         result:=false;
125         with instr.oper[opidx]^ do
126           begin
127             case typ of
128               top_para:
129                 begin
130                   for paracnt:=0 to paras.count-1 do
131                     begin
132                       callpara:=pllvmcallpara(paras[paracnt]);
133                       if (callpara^.loc in [LOC_REGISTER,LOC_FPUREGISTER,LOC_MMREGISTER]) and
134                          (getregtype(callpara^.reg)=regtype) then
135                         begin
136                           result:=addreginfo(regs,r,callpara^.reg,operand_read) or result;
137                           break
138                         end;
139                     end;
140                 end;
141               else
142                 result:=inherited;
143             end;
144           end;
145       end;
146 
147 
148     procedure trgllvm.substitute_spilled_registers(const regs: tspillregsinfo; instr: tai_cpu_abstract_sym; opidx: longint);
149       var
150         i, paracnt: longint;
151         callpara: pllvmcallpara;
152       begin
153         with instr.oper[opidx]^ do
154           case typ of
155             top_para:
156               begin
157                 for paracnt:=0 to paras.count-1 do
158                   begin
159                     callpara:=pllvmcallpara(paras[paracnt]);
160                     if (callpara^.loc in [LOC_REGISTER,LOC_FPUREGISTER,LOC_MMREGISTER]) and
161                        (getregtype(callpara^.reg)=regtype) then
162                       try_replace_reg(regs, callpara^.reg,true);
163                   end;
164               end;
165             else
166               inherited;
167           end;
168       end;
169 
170 
171      procedure trgllvm.determine_spill_registers(list: TasmList; headertai: tai);
172        var
173          hp: tai;
174          reg: tregister;
175          sr: tsuperregister;
176          i: longint;
177        begin
178          spillednodes.clear;
179          { there should be only one round of spilling per register type, we
180            shouldn't generate multiple writes to a single register here }
181          if spillcounter<>0 then
182            exit;
183          { registers must be in SSA form -> determine all registers that are
184            written to more than once }
185          hp:=headertai;
186          { 2 bits per superregister, rounded up to a byte }
187          writtenregs:=allocmem((maxreg*bitsizeof(twrittenregs[low(tsuperregister)])+7) shr 3);
188          while assigned(hp) do
189            begin
190              case hp.typ of
191                ait_llvmins:
192                  begin
193                    for i:=0 to taillvm(hp).ops-1 do
194                      if (taillvm(hp).oper[i]^.typ=top_reg) and
195                         (getregtype(taillvm(hp).oper[i]^.reg)=regtype)  and
196                         (taillvm(hp).spilling_get_operation_type(i)=operand_write) then
197                        begin
198                          reg:=taillvm(hp).oper[i]^.reg;
199                          sr:=getsupreg(reg);
200                          if writtenregs^[sr]<rw_multiple then
201                            writtenregs^[sr]:=succ(writtenregs^[sr]);
202                        end;
203                  end;
204              end;
205              hp:=tai(hp.next);
206            end;
207          { add all registers with multiple writes to the spilled nodes }
208          for sr:=0 to maxreg-1 do
209            if writtenregs^[sr]=rw_multiple then
210              spillednodes.add(sr);
211          freemem(writtenregs);
212        end;
213 
214 
215     procedure trgllvm.get_spill_temp(list: TAsmlist; spill_temps: Pspill_temp_list; supreg: tsuperregister);
216       var
217         supstart: tai;
218         i, paracnt: longint;
219         def: tdef;
220         callpara: pllvmcallpara;
221       begin
222         supstart:=live_start[supreg];
223         if supstart.typ<>ait_llvmins then
224           internalerror(2013110701);
225         { determine type of register so we can allocate a temp of the right
226           type }
227         def:=nil;
228         for i:=0 to taillvm(supstart).ops-1 do
229           begin
230             case taillvm(supstart).oper[i]^.typ of
231               top_reg:
232                 if (getregtype(taillvm(supstart).oper[i]^.reg)=regtype) and
233                    (getsupreg(taillvm(supstart).oper[i]^.reg)=supreg) then
234                   begin
235                     def:=taillvm(supstart).spilling_get_reg_type(i);
236                     break
237                   end;
238               top_para:
239                 begin
240                   for paracnt:=0 to taillvm(supstart).oper[i]^.paras.count-1 do
241                     begin
242                       callpara:=pllvmcallpara(taillvm(supstart).oper[i]^.paras[paracnt]);
243                       if (callpara^.loc in [LOC_REGISTER,LOC_FPUREGISTER,LOC_MMREGISTER]) and
244                          (getregtype(callpara^.reg)=regtype) and
245                          (getsupreg(callpara^.reg)=supreg) then
246                         begin
247                           def:=callpara^.def;
248                           break
249                         end;
250                     end;
251                 end;
252             end;
253           end;
254         if not assigned(def) then
255           internalerror(2013110702);
256         tg.gethltemp(list,def,def.size,tt_noreuse,spill_temps^[supreg]);
257         { record for use in spill instructions }
258         reginfo[supreg].def:=def;
259       end;
260 
261 end.
262