nllvmtcon.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. {
  2. Copyright (c) 2014 by Jonas Maebe
  3. Generates code for typed constant declarations for the LLVM target
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. unit nllvmtcon;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. cclasses,constexp,globtype,
  22. aasmbase,aasmtai,aasmcnst,aasmllvm,
  23. symconst,symtype,symdef,symsym,
  24. ngtcon;
  25. type
  26. tllvmaggregateinformation = class(taggregateinformation)
  27. private
  28. faggai: tai_aggregatetypedconst;
  29. fanonrecalignpos: longint;
  30. public
  31. constructor create(_def: tdef; _typ: ttypedconstkind); override;
  32. property aggai: tai_aggregatetypedconst read faggai write faggai;
  33. property anonrecalignpos: longint read fanonrecalignpos write fanonrecalignpos;
  34. end;
  35. tllvmtai_typedconstbuilder = class(ttai_typedconstbuilder)
  36. protected type
  37. public
  38. { set the default value for caggregateinformation (= tllvmaggregateinformation) }
  39. class constructor classcreate;
  40. protected
  41. fqueued_def: tdef;
  42. fqueued_tai,
  43. flast_added_tai: tai;
  44. fqueued_tai_opidx: longint;
  45. procedure finalize_asmlist(sym: tasmsymbol; def: tdef; section: TAsmSectiontype; const secname: TSymStr; alignment: shortint; const options: ttcasmlistoptions); override;
  46. { outerai: the ai that should become fqueued_tai in case it's still nil,
  47. or that should be filled in the fqueued_tai_opidx of the current
  48. fqueued_tai if it's not nil
  49. innerai: the innermost ai (possibly an operand of outerai) in which
  50. newindex indicates which operand is empty and can be filled with the
  51. next queued tai }
  52. procedure update_queued_tai(resdef: tdef; outerai, innerai: tai; newindex: longint);
  53. function wrap_with_type(p: tai; def: tdef): tai;
  54. procedure do_emit_tai(p: tai; def: tdef); override;
  55. procedure mark_anon_aggregate_alignment; override;
  56. procedure insert_marked_aggregate_alignment(def: tdef); override;
  57. procedure begin_aggregate_internal(def: tdef; anonymous: boolean); override;
  58. procedure end_aggregate_internal(def: tdef; anonymous: boolean); override;
  59. public
  60. constructor create; override;
  61. destructor destroy; override;
  62. procedure emit_tai_procvar2procdef(p: tai; pvdef: tprocvardef); override;
  63. procedure queue_init(todef: tdef); override;
  64. procedure queue_vecn(def: tdef; const index: tconstexprint); override;
  65. procedure queue_subscriptn(def: tabstractrecorddef; vs: tfieldvarsym); override;
  66. procedure queue_typeconvn(fromdef, todef: tdef); override;
  67. procedure queue_emit_staticvar(vs: tstaticvarsym); override;
  68. procedure queue_emit_asmsym(sym: tasmsymbol; def: tdef); override;
  69. class function get_string_symofs(typ: tstringtype; winlikewidestring: boolean): pint; override;
  70. end;
  71. tllvmasmlisttypedconstbuilder = class(tasmlisttypedconstbuilder)
  72. protected
  73. procedure tc_emit_string_offset(const ll: tasmlabofs; const strlength: longint; const st: tstringtype; const winlikewidestring: boolean; const charptrdef: tdef); override;
  74. end;
  75. implementation
  76. uses
  77. verbose,
  78. aasmdata,
  79. cpubase,llvmbase,
  80. symbase,symtable,llvmdef,defutil;
  81. { tllvmaggregateinformation }
  82. constructor tllvmaggregateinformation.create(_def: tdef; _typ: ttypedconstkind);
  83. begin
  84. inherited;
  85. fanonrecalignpos:=-1;
  86. end;
  87. class constructor tllvmtai_typedconstbuilder.classcreate;
  88. begin
  89. caggregateinformation:=tllvmaggregateinformation;
  90. end;
  91. procedure tllvmtai_typedconstbuilder.finalize_asmlist(sym: tasmsymbol; def: tdef; section: TAsmSectiontype; const secname: TSymStr; alignment: shortint; const options: ttcasmlistoptions);
  92. var
  93. newasmlist: tasmlist;
  94. begin
  95. { todo }
  96. if section = sec_user then
  97. internalerror(2014052904);
  98. newasmlist:=tasmlist.create;
  99. { llvm declaration with as initialisation data all the elements from the
  100. original asmlist }
  101. newasmlist.concat(taillvmdecl.create(sym,def,fasmlist,section,alignment));
  102. fasmlist:=newasmlist;
  103. end;
  104. procedure tllvmtai_typedconstbuilder.update_queued_tai(resdef: tdef; outerai, innerai: tai; newindex: longint);
  105. begin
  106. { the outer tai must always be a typed constant (possibly a wrapper
  107. around a taillvm or so), in order for result type information to be
  108. available }
  109. if outerai.typ<>ait_typedconst then
  110. internalerror(2014060401);
  111. { is the result of the outermost expression different from the type of
  112. this typed const? -> insert type conversion }
  113. if not assigned(fqueued_tai) and
  114. (resdef<>fqueued_def) and
  115. (llvmencodetype(resdef)<>llvmencodetype(fqueued_def)) then
  116. queue_typeconvn(resdef,fqueued_def);
  117. if assigned(fqueued_tai) then
  118. begin
  119. taillvm(flast_added_tai).loadtai(fqueued_tai_opidx,outerai);
  120. { already flushed? }
  121. if fqueued_tai_opidx=-1 then
  122. internalerror(2014062201);
  123. end
  124. else
  125. begin
  126. fqueued_tai:=outerai;
  127. fqueued_def:=resdef;
  128. end;
  129. fqueued_tai_opidx:=newindex;
  130. flast_added_tai:=innerai;
  131. end;
  132. function tllvmtai_typedconstbuilder.wrap_with_type(p: tai; def: tdef): tai;
  133. begin
  134. result:=tai_simpletypedconst.create(tck_simple,def,p);
  135. end;
  136. constructor tllvmtai_typedconstbuilder.create;
  137. begin
  138. inherited create;
  139. end;
  140. destructor tllvmtai_typedconstbuilder.destroy;
  141. begin
  142. inherited destroy;
  143. end;
  144. procedure tllvmtai_typedconstbuilder.do_emit_tai(p: tai; def: tdef);
  145. var
  146. ai: tai;
  147. stc: tai_abstracttypedconst;
  148. kind: ttypedconstkind;
  149. info: tllvmaggregateinformation;
  150. begin
  151. if assigned(fqueued_tai) then
  152. begin
  153. kind:=tck_simple;
  154. { finalise the queued expression }
  155. ai:=tai_simpletypedconst.create(kind,def,p);
  156. { set the new index to -1, so we internalerror should we try to
  157. add anything further }
  158. update_queued_tai(def,ai,ai,-1);
  159. { and emit it }
  160. stc:=tai_abstracttypedconst(fqueued_tai);
  161. def:=fqueued_def;
  162. { ensure we don't try to emit this one again }
  163. fqueued_tai:=nil;
  164. end
  165. else
  166. stc:=tai_simpletypedconst.create(tck_simple,def,p);
  167. info:=tllvmaggregateinformation(curagginfo);
  168. { these elements can be aggregates themselves, e.g. a shortstring can
  169. be emitted as a series of bytes and string data arrays }
  170. kind:=aggregate_kind(def);
  171. if (kind<>tck_simple) then
  172. begin
  173. if not assigned(info) or
  174. (info.aggai.adetyp<>kind) then
  175. internalerror(2014052906);
  176. end;
  177. if assigned(info) then
  178. info.aggai.addvalue(stc)
  179. else
  180. inherited do_emit_tai(stc,def);
  181. end;
  182. procedure tllvmtai_typedconstbuilder.mark_anon_aggregate_alignment;
  183. var
  184. info: tllvmaggregateinformation;
  185. begin
  186. info:=tllvmaggregateinformation(curagginfo);
  187. info.anonrecalignpos:=info.aggai.valuecount;
  188. end;
  189. procedure tllvmtai_typedconstbuilder.insert_marked_aggregate_alignment(def: tdef);
  190. var
  191. info: tllvmaggregateinformation;
  192. fillbytes: asizeint;
  193. begin
  194. info:=tllvmaggregateinformation(curagginfo);
  195. if info.anonrecalignpos=-1 then
  196. internalerror(2014091501);
  197. fillbytes:=info.prepare_next_field(def);
  198. while fillbytes>0 do
  199. begin
  200. info.aggai.insertvaluebeforepos(tai_simpletypedconst.create(tck_simple,u8inttype,tai_const.create_8bit(0)),info.anonrecalignpos);
  201. dec(fillbytes);
  202. end;
  203. end;
  204. procedure tllvmtai_typedconstbuilder.emit_tai_procvar2procdef(p: tai; pvdef: tprocvardef);
  205. begin
  206. if not pvdef.is_addressonly then
  207. pvdef:=tprocvardef(pvdef.getcopyas(procvardef,pc_address_only));
  208. emit_tai(p,pvdef);
  209. end;
  210. procedure tllvmtai_typedconstbuilder.begin_aggregate_internal(def: tdef; anonymous: boolean);
  211. var
  212. agg: tai_aggregatetypedconst;
  213. tck: ttypedconstkind;
  214. curagg: tllvmaggregateinformation;
  215. begin
  216. tck:=aggregate_kind(def);
  217. if tck<>tck_simple then
  218. begin
  219. { create new typed const aggregate }
  220. agg:=tai_aggregatetypedconst.create(tck,def);
  221. { either add to the current typed const aggregate (if nested), or
  222. emit to the asmlist (if top level) }
  223. curagg:=tllvmaggregateinformation(curagginfo);
  224. if assigned(curagg) then
  225. curagg.aggai.addvalue(agg)
  226. else
  227. fasmlist.concat(agg);
  228. { create aggregate information for this new aggregate }
  229. inherited;
  230. { set new current typed const aggregate }
  231. tllvmaggregateinformation(curagginfo).aggai:=agg
  232. end
  233. else
  234. inherited;
  235. end;
  236. procedure tllvmtai_typedconstbuilder.end_aggregate_internal(def: tdef; anonymous: boolean);
  237. var
  238. info: tllvmaggregateinformation;
  239. begin
  240. if aggregate_kind(def)<>tck_simple then
  241. begin
  242. info:=tllvmaggregateinformation(curagginfo);
  243. if not assigned(info) then
  244. internalerror(2014060101);
  245. info.aggai.finish;
  246. end;
  247. inherited;
  248. end;
  249. procedure tllvmtai_typedconstbuilder.queue_init(todef: tdef);
  250. begin
  251. inherited;
  252. fqueued_tai:=nil;
  253. flast_added_tai:=nil;
  254. fqueued_tai_opidx:=-1;
  255. fqueued_def:=todef;
  256. end;
  257. procedure tllvmtai_typedconstbuilder.queue_vecn(def: tdef; const index: tconstexprint);
  258. var
  259. ai: taillvm;
  260. aityped: tai;
  261. eledef: tdef;
  262. begin
  263. { update range checking info }
  264. inherited;
  265. ai:=taillvm.getelementptr_reg_tai_size_const(NR_NO,nil,ptrsinttype,index.svalue,true);
  266. case def.typ of
  267. arraydef:
  268. eledef:=tarraydef(def).elementdef;
  269. stringdef:
  270. case tstringdef(def).stringtype of
  271. st_shortstring,
  272. st_longstring,
  273. st_ansistring:
  274. eledef:=cansichartype;
  275. st_widestring,
  276. st_unicodestring:
  277. eledef:=cwidechartype;
  278. else
  279. internalerror(2014062202);
  280. end;
  281. else
  282. internalerror(2014062203);
  283. end;
  284. aityped:=wrap_with_type(ai,getpointerdef(eledef));
  285. update_queued_tai(getpointerdef(eledef),aityped,ai,1);
  286. end;
  287. procedure tllvmtai_typedconstbuilder.queue_subscriptn(def: tabstractrecorddef; vs: tfieldvarsym);
  288. var
  289. getllvmfieldaddr,
  290. getpascalfieldaddr,
  291. getllvmfieldaddrtyped: tai;
  292. llvmfielddef: tdef;
  293. begin
  294. { update range checking info }
  295. inherited;
  296. llvmfielddef:=tabstractrecordsymtable(def.symtable).llvmst[vs.llvmfieldnr].def;
  297. { get the address of the llvm-struct field that corresponds to this
  298. Pascal field }
  299. getllvmfieldaddr:=taillvm.getelementptr_reg_tai_size_const(NR_NO,nil,s32inttype,vs.llvmfieldnr,true);
  300. { getelementptr doesn't contain its own resultdef, so encode it via a
  301. tai_simpletypedconst tai }
  302. getllvmfieldaddrtyped:=wrap_with_type(getllvmfieldaddr,getpointerdef(llvmfielddef));
  303. { if it doesn't match the requested field exactly (variant record),
  304. fixup the result }
  305. getpascalfieldaddr:=getllvmfieldaddrtyped;
  306. if (vs.offsetfromllvmfield<>0) or
  307. (llvmfielddef<>vs.vardef) then
  308. begin
  309. { offset of real field relative to llvm-struct field <> 0? }
  310. if vs.offsetfromllvmfield<>0 then
  311. begin
  312. { convert to a pointer to a 1-sized element }
  313. if llvmfielddef.size<>1 then
  314. begin
  315. getpascalfieldaddr:=taillvm.op_reg_tai_size(la_bitcast,NR_NO,getpascalfieldaddr,u8inttype);
  316. { update the current fielddef of the expression }
  317. llvmfielddef:=u8inttype;
  318. end;
  319. { add the offset }
  320. getpascalfieldaddr:=taillvm.getelementptr_reg_tai_size_const(NR_NO,getpascalfieldaddr,ptrsinttype,vs.offsetfromllvmfield,true);
  321. { ... and set the result type of the getelementptr }
  322. getpascalfieldaddr:=wrap_with_type(getpascalfieldaddr,getpointerdef(u8inttype));
  323. llvmfielddef:=u8inttype;
  324. end;
  325. { bitcast the data at the final offset to the right type }
  326. if llvmfielddef<>vs.vardef then
  327. getpascalfieldaddr:=wrap_with_type(taillvm.op_reg_tai_size(la_bitcast,NR_NO,getpascalfieldaddr,getpointerdef(vs.vardef)),getpointerdef(vs.vardef));
  328. end;
  329. update_queued_tai(getpointerdef(vs.vardef),getpascalfieldaddr,getllvmfieldaddr,1);
  330. end;
  331. procedure tllvmtai_typedconstbuilder.queue_typeconvn(fromdef, todef: tdef);
  332. var
  333. ai: taillvm;
  334. typedai: tai;
  335. tmpintdef: tdef;
  336. op,
  337. firstop,
  338. secondop: tllvmop;
  339. begin
  340. inherited;
  341. { special case: procdef -> procvardef/pointerdef: must take address of
  342. the procdef }
  343. if (fromdef.typ=procdef) and
  344. (todef.typ<>procdef) then
  345. fromdef:=tprocdef(fromdef).getcopyas(procvardef,pc_address_only);
  346. op:=llvmconvop(fromdef,todef);
  347. case op of
  348. la_ptrtoint_to_x,
  349. la_x_to_inttoptr:
  350. begin
  351. { convert via an integer with the same size as "x" }
  352. if op=la_ptrtoint_to_x then
  353. begin
  354. tmpintdef:=cgsize_orddef(def_cgsize(todef));
  355. firstop:=la_ptrtoint;
  356. secondop:=la_bitcast
  357. end
  358. else
  359. begin
  360. tmpintdef:=cgsize_orddef(def_cgsize(fromdef));
  361. firstop:=la_bitcast;
  362. secondop:=la_inttoptr;
  363. end;
  364. { since we have to queue operations from outer to inner, first queue
  365. the conversion from the tempintdef to the todef }
  366. ai:=taillvm.op_reg_tai_size(secondop,NR_NO,nil,todef);
  367. typedai:=wrap_with_type(ai,todef);
  368. update_queued_tai(todef,typedai,ai,1);
  369. todef:=tmpintdef;
  370. op:=firstop
  371. end;
  372. end;
  373. ai:=taillvm.op_reg_tai_size(op,NR_NO,nil,todef);
  374. typedai:=wrap_with_type(ai,todef);
  375. update_queued_tai(todef,typedai,ai,1);
  376. end;
  377. procedure tllvmtai_typedconstbuilder.queue_emit_staticvar(vs: tstaticvarsym);
  378. begin
  379. { we've already incorporated the offset via the inserted operations above,
  380. make sure it doesn't get emitted again as part of the tai_const for
  381. the tasmsymbol }
  382. fqueue_offset:=0;
  383. inherited;
  384. end;
  385. procedure tllvmtai_typedconstbuilder.queue_emit_asmsym(sym: tasmsymbol; def: tdef);
  386. begin
  387. { we've already incorporated the offset via the inserted operations above,
  388. make sure it doesn't get emitted again as part of the tai_const for
  389. the tasmsymbol }
  390. fqueue_offset:=0;
  391. inherited;
  392. end;
  393. class function tllvmtai_typedconstbuilder.get_string_symofs(typ: tstringtype; winlikewidestring: boolean): pint;
  394. begin
  395. { LLVM does not support labels in the middle of a declaration }
  396. result:=get_string_header_size(typ,winlikewidestring);
  397. end;
  398. { tllvmasmlisttypedconstbuilder }
  399. procedure tllvmasmlisttypedconstbuilder.tc_emit_string_offset(const ll: tasmlabofs; const strlength: longint; const st: tstringtype; const winlikewidestring: boolean; const charptrdef: tdef);
  400. var
  401. srsym : tsym;
  402. srsymtable: tsymtable;
  403. strrecdef : trecorddef;
  404. offset: pint;
  405. field: tfieldvarsym;
  406. dataptrdef: tdef;
  407. begin
  408. { if the returned offset is <> 0, then the string data
  409. starts at that offset -> translate to a field for the
  410. high level code generator }
  411. if ll.ofs<>0 then
  412. begin
  413. { get the recorddef for this string constant }
  414. if not searchsym_type(ctai_typedconstbuilder.get_dynstring_rec_name(st,winlikewidestring,strlength),srsym,srsymtable) then
  415. internalerror(2014080406);
  416. strrecdef:=trecorddef(ttypesym(srsym).typedef);
  417. { offset in the record of the the string data }
  418. offset:=ctai_typedconstbuilder.get_string_symofs(st,winlikewidestring);
  419. { field corresponding to this offset }
  420. field:=trecordsymtable(strrecdef.symtable).findfieldbyoffset(offset);
  421. { pointerdef to the string data array }
  422. dataptrdef:=getpointerdef(field.vardef);
  423. ftcb.queue_init(charptrdef);
  424. ftcb.queue_addrn(dataptrdef,charptrdef);
  425. ftcb.queue_subscriptn(strrecdef,field);
  426. ftcb.queue_emit_asmsym(ll.lab,strrecdef);
  427. end
  428. else
  429. { since llvm doesn't support labels in the middle of structs, this
  430. offset should never be 0 }
  431. internalerror(2014080506);
  432. end;
  433. begin
  434. ctai_typedconstbuilder:=tllvmtai_typedconstbuilder;
  435. ctypedconstbuilder:=tllvmasmlisttypedconstbuilder;
  436. end.