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