1 {
2     Copyright (c) 2015 by Jonas Maebe
3 
4     This unit implements llvm support for some basic nodes
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 
20  ****************************************************************************
21 }
22 unit nllvmbas;
23 
24 {$i fpcdefs.inc}
25 
26 interface
27 
28     uses
29        globtype,cclasses,
30        aasmbase,aasmtai,aasmdata,
31        nbas,ncgbas,
32        symsym;
33 
34     type
35        tllvmasmnode = class(tcgasmnode)
36         protected
37          { map a tasmsymbol to an index in fsymboldata }
38          fsymbollookup: THashSet;
39          { the LLVM symbolic inline assembly operands to which the ones in the
40            source code are mapped }
41          fsymboldata: tfplist;
getllvmasmopindexforsymnull42          function getllvmasmopindexforsym(sym: tabstractnormalvarsym): longint;
getllvmasmparasymnull43          function getllvmasmparasym(sym: tabstractnormalvarsym): tasmsymbol;
44          procedure ResolveRef(const filepos: tfileposinfo; var op: toper); override;
45         public
46          constructor create(p : TAsmList); override;
47          destructor destroy; override;
48          procedure pass_generate_code; override;
49        end;
50 
51       tllvmtempinfoaccessor = class(ttempinfoaccessor)
52        protected
53         class procedure settempinfoflags(tempinfo: ptempinfo; const flags: ttempinfoflags); override;
54       end;
55 
56        tllvmtempcreatenode = class(tcgtempcreatenode)
57           procedure pass_generate_code;override;
58        end;
59 
60   implementation
61 
62     uses
63       verbose,cutils,
64       cgbase,cgutils,paramgr,
65       symconst,symdef,procinfo,
66       node,
67       cpubase,llvmbase,aasmllvm
68       ;
69 
70 {*****************************************************************************
71                              TLLVMASMNODE
72 *****************************************************************************}
73 
tllvmasmnode.getllvmasmopindexforsymnull74     function tllvmasmnode.getllvmasmopindexforsym(sym: tabstractnormalvarsym): longint;
75       var
76         key: record
77           sym: pointer;
78         end;
79         res: PHashSetItem;
80         callpara: pllvmcallpara;
81       begin
82         key.sym:=sym;
83         res:=fsymbollookup.FindOrAdd(@key,sizeof(key));
84         if not assigned(res^.Data) then
85           begin
86             new(callpara);
87             callpara^.def:=cpointerdef.getreusable(sym.vardef);
88             if (sym.typ=paravarsym) and
89                paramanager.push_addr_param(sym.varspez,sym.vardef,current_procinfo.procdef.proccalloption) then
90               callpara^.def:=cpointerdef.getreusable(callpara^.def);
91             callpara^.sret:=false;
92             callpara^.byval:=false;
93             callpara^.valueext:=lve_none;
94             callpara^.loc:=LOC_REGISTER;
95             { address must be a temp register }
96             if (sym.localloc.loc<>LOC_REFERENCE) or
97                (sym.localloc.reference.base=NR_NO) or
98                (sym.localloc.reference.index<>NR_NO) or
99                (sym.localloc.reference.offset<>0) or
100                assigned(sym.localloc.reference.symbol) then
101               internalerror(2016111001);
102             callpara^.reg:=sym.localloc.reference.base;
103             fsymboldata.add(callpara);
104             ptruint(res^.Data):=fsymboldata.count-1;
105           end;
106         result:=longint(ptruint(res^.Data));
107       end;
108 
109 
tllvmasmnode.getllvmasmparasymnull110     function tllvmasmnode.getllvmasmparasym(sym: tabstractnormalvarsym): tasmsymbol;
111       begin
112         { these have to be transformed from ^nr into into $nr; we use ^ because
113           we also have to double all other occurrences of '$' in the assembly
114           code, and we can't differentiate between these and other '$'s in
115           agllvm }
116         result:=current_asmdata.RefAsmSymbol('^'+tostr(getllvmasmopindexforsym(sym)),AT_DATA,false);
117       end;
118 
119 
120     procedure tllvmasmnode.ResolveRef(const filepos: tfileposinfo; var op: toper);
121       var
122         sym: tabstractnormalvarsym;
123         ref: treference;
124         sofs: pint;
125         indexreg : tregister;
126         getoffset: boolean;
127 {$ifdef x86}
128         scale : byte;
129 {$endif x86}
130       begin
131         { pure assembler routines are handled by the regular code generator }
132         if po_assembler in current_procinfo.procdef.procoptions then
133           begin
134             inherited;
135             exit;
136           end;
137         { translate all symbolic references to "parameters" of the llvm
138           assembler statements }
139         case op.typ of
140           top_local:
141             begin
142               sofs:=op.localoper^.localsymofs;
143               indexreg:=op.localoper^.localindexreg;
144 {$ifdef x86}
145               scale:=op.localoper^.localscale;
146 {$endif x86}
147               getoffset:=op.localoper^.localgetoffset;
148               sym:=tabstractnormalvarsym(op.localoper^.localsym);
149               dispose(op.localoper);
150               case sym.localloc.loc of
151                 LOC_REFERENCE:
152                   begin
153                     if getoffset then
154                       begin
155                         { todo: print proper error. You cannot get the offset
156                           of a local variable since it may be in a register
157                           outside the assembler block with llvm }
158                         internalerror(2016102001);
159                       end
160                     else
161                       begin
162                         op.typ:=top_ref;
163                         new(op.ref);
164                         reference_reset_symbol(op.ref^,getllvmasmparasym(sym),sofs,
165                           newalignment(sym.localloc.reference.alignment,sofs),[]);
166                         op.ref^.index:=indexreg;
167 {$ifdef x86}
168                         op.ref^.scalefactor:=scale;
169 {$endif x86}
170                       end;
171                   end
172                 else
173                   { all locals accessed from assembler are forced into memory
174                     by FPC }
175                   internalerror(2016101506);
176               end;
177             end;
178         end;
179       end;
180 
181 
182     constructor tllvmasmnode.create(p: TAsmList);
183       begin
184         inherited;
185       end;
186 
187 
188     destructor tllvmasmnode.destroy;
189       begin
190         { normally already freed in pass_generate_code, but in case an error
191           occurred that may not have happened }
192         fsymboldata.free;
193         fsymbollookup.free;
194         inherited;
195       end;
196 
197 
198     procedure tllvmasmnode.pass_generate_code;
199       var
200         oldasmlist: tasmlist;
201         asmai: tai;
202       begin
203         oldasmlist:=nil;
204         if not(po_assembler in current_procinfo.procdef.procoptions) and
205            not(nf_get_asm_position in flags) then
206           begin
207             { store the assembler code in a separate list, so we can make it
208               the argument of an asmblock instruction }
209             oldasmlist:=current_asmdata.CurrAsmList;
210             current_asmdata.CurrAsmList:=tasmlist.create;
211             { record relation between parameters and replaced local assembler
212               operands }
213             fsymboldata:=tfplist.create;
214             fsymbollookup:=THashSet.Create(8,True,False);
215           end;
216         inherited;
217         if not(po_assembler in current_procinfo.procdef.procoptions) and
218            not(nf_get_asm_position in flags) then
219           begin
220             asmai:=taillvm.asm_paras(current_asmdata.CurrAsmList,fsymboldata);
221             fsymboldata:=nil;
222             fsymbollookup.free;
223             fsymbollookup:=nil;
224             oldasmlist.concat(asmai);
225             current_asmdata.CurrAsmList:=oldasmlist;
226           end;
227       end;
228 
229 {*****************************************************************************
230                           TLLVMTEMPINFOACCESSOR
231 *****************************************************************************}
232 
233     class procedure tllvmtempinfoaccessor.settempinfoflags(tempinfo: ptempinfo; const flags: ttempinfoflags);
234         begin
235           { it is not possible to typecast between e.g. an integer and a record
236             in a register, which is a problem if such a typecast is performed on
237             an lvalue (since we then have to store it first to a temp in memory,
238             which means we no longer have an lvalue).
239 
240             Disable regvars altogether since LLVM will put the values in registers
241             anyway if possible/useful. }
242           inherited settempinfoflags(tempinfo,flags-[ti_may_be_in_reg]);
243         end;
244 
245 
246 {*****************************************************************************
247                           TTEMPCREATENODE
248 *****************************************************************************}
249 
250     procedure tllvmtempcreatenode.pass_generate_code;
251       begin
252         inherited;
253 
254         { if a temp is in a register and we never assign anything to it (e.g.
255           because it's the register for an inlined function result that never
256           gets assigned a value), then llvm will be confused the first time
257           we try to read from it (since it's never been defined) -> always
258           immediately assign undef to such registers }
259         if tempinfo^.location.loc in [LOC_REGISTER,LOC_CREGISTER,LOC_FPUREGISTER,
260              LOC_CFPUREGISTER,LOC_MMREGISTER,LOC_CMMREGISTER] then
261           current_asmdata.CurrAsmList.concat(
262             taillvm.op_reg_size_undef(la_bitcast,tempinfo^.location.register,tempinfo^.typedef)
263           );
264       end;
265 
266 
267 begin
268    casmnode:=tllvmasmnode;
269    ctempinfoaccessor:=tllvmtempinfoaccessor;
270    ctempcreatenode:=tllvmtempcreatenode;
271 end.
272