nllvmtcon.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  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. destructor destroy; override;
  61. procedure emit_tai_procvar2procdef(p: tai; pvdef: tprocvardef); override;
  62. procedure emit_string_offset(const ll: tasmlabofs; const strlength: longint; const st: tstringtype; const winlikewidestring: boolean; const charptrdef: tdef); 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. procedure queue_emit_ordconst(value: int64; def: tdef); override;
  70. class function get_string_symofs(typ: tstringtype; winlikewidestring: boolean): pint; override;
  71. end;
  72. implementation
  73. uses
  74. verbose,
  75. aasmdata,
  76. cpubase,llvmbase,
  77. symbase,symtable,llvmdef,defutil;
  78. { tllvmaggregateinformation }
  79. constructor tllvmaggregateinformation.create(_def: tdef; _typ: ttypedconstkind);
  80. begin
  81. inherited;
  82. fanonrecalignpos:=-1;
  83. end;
  84. class constructor tllvmtai_typedconstbuilder.classcreate;
  85. begin
  86. caggregateinformation:=tllvmaggregateinformation;
  87. end;
  88. procedure tllvmtai_typedconstbuilder.finalize_asmlist(sym: tasmsymbol; def: tdef; section: TAsmSectiontype; const secname: TSymStr; alignment: shortint; const options: ttcasmlistoptions);
  89. var
  90. newasmlist: tasmlist;
  91. begin
  92. { todo }
  93. if section = sec_user then
  94. internalerror(2014052904);
  95. newasmlist:=tasmlist.create;
  96. { llvm declaration with as initialisation data all the elements from the
  97. original asmlist }
  98. newasmlist.concat(taillvmdecl.create(sym,def,fasmlist,section,alignment));
  99. fasmlist:=newasmlist;
  100. end;
  101. procedure tllvmtai_typedconstbuilder.update_queued_tai(resdef: tdef; outerai, innerai: tai; newindex: longint);
  102. begin
  103. { the outer tai must always be a typed constant (possibly a wrapper
  104. around a taillvm or so), in order for result type information to be
  105. available }
  106. if outerai.typ<>ait_typedconst then
  107. internalerror(2014060401);
  108. { is the result of the outermost expression different from the type of
  109. this typed const? -> insert type conversion }
  110. if not assigned(fqueued_tai) and
  111. (resdef<>fqueued_def) and
  112. (llvmencodetype(resdef)<>llvmencodetype(fqueued_def)) then
  113. queue_typeconvn(resdef,fqueued_def);
  114. if assigned(fqueued_tai) then
  115. begin
  116. taillvm(flast_added_tai).loadtai(fqueued_tai_opidx,outerai);
  117. { already flushed? }
  118. if fqueued_tai_opidx=-1 then
  119. internalerror(2014062201);
  120. end
  121. else
  122. begin
  123. fqueued_tai:=outerai;
  124. fqueued_def:=resdef;
  125. end;
  126. fqueued_tai_opidx:=newindex;
  127. flast_added_tai:=innerai;
  128. end;
  129. function tllvmtai_typedconstbuilder.wrap_with_type(p: tai; def: tdef): tai;
  130. begin
  131. result:=tai_simpletypedconst.create(tck_simple,def,p);
  132. end;
  133. destructor tllvmtai_typedconstbuilder.destroy;
  134. begin
  135. inherited destroy;
  136. end;
  137. procedure tllvmtai_typedconstbuilder.do_emit_tai(p: tai; def: tdef);
  138. var
  139. ai: tai;
  140. stc: tai_abstracttypedconst;
  141. kind: ttypedconstkind;
  142. info: tllvmaggregateinformation;
  143. begin
  144. if assigned(fqueued_tai) then
  145. begin
  146. kind:=tck_simple;
  147. { finalise the queued expression }
  148. ai:=tai_simpletypedconst.create(kind,def,p);
  149. { set the new index to -1, so we internalerror should we try to
  150. add anything further }
  151. update_queued_tai(def,ai,ai,-1);
  152. { and emit it }
  153. stc:=tai_abstracttypedconst(fqueued_tai);
  154. def:=fqueued_def;
  155. { ensure we don't try to emit this one again }
  156. fqueued_tai:=nil;
  157. end
  158. else
  159. stc:=tai_simpletypedconst.create(tck_simple,def,p);
  160. info:=tllvmaggregateinformation(curagginfo);
  161. { these elements can be aggregates themselves, e.g. a shortstring can
  162. be emitted as a series of bytes and string data arrays }
  163. kind:=aggregate_kind(def);
  164. if (kind<>tck_simple) then
  165. begin
  166. if not assigned(info) or
  167. (info.aggai.adetyp<>kind) then
  168. internalerror(2014052906);
  169. end;
  170. if assigned(info) then
  171. info.aggai.addvalue(stc)
  172. else
  173. inherited do_emit_tai(stc,def);
  174. end;
  175. procedure tllvmtai_typedconstbuilder.mark_anon_aggregate_alignment;
  176. var
  177. info: tllvmaggregateinformation;
  178. begin
  179. info:=tllvmaggregateinformation(curagginfo);
  180. info.anonrecalignpos:=info.aggai.valuecount;
  181. end;
  182. procedure tllvmtai_typedconstbuilder.insert_marked_aggregate_alignment(def: tdef);
  183. var
  184. info: tllvmaggregateinformation;
  185. fillbytes: asizeint;
  186. begin
  187. info:=tllvmaggregateinformation(curagginfo);
  188. if info.anonrecalignpos=-1 then
  189. internalerror(2014091501);
  190. fillbytes:=info.prepare_next_field(def);
  191. while fillbytes>0 do
  192. begin
  193. info.aggai.insertvaluebeforepos(tai_simpletypedconst.create(tck_simple,u8inttype,tai_const.create_8bit(0)),info.anonrecalignpos);
  194. dec(fillbytes);
  195. end;
  196. end;
  197. procedure tllvmtai_typedconstbuilder.emit_tai_procvar2procdef(p: tai; pvdef: tprocvardef);
  198. begin
  199. if not pvdef.is_addressonly then
  200. pvdef:=tprocvardef(pvdef.getcopyas(procvardef,pc_address_only));
  201. emit_tai(p,pvdef);
  202. end;
  203. procedure tllvmtai_typedconstbuilder.emit_string_offset(const ll: tasmlabofs; const strlength: longint; const st: tstringtype; const winlikewidestring: boolean; const charptrdef: tdef);
  204. var
  205. srsym : tsym;
  206. srsymtable: tsymtable;
  207. strrecdef : trecorddef;
  208. offset: pint;
  209. field: tfieldvarsym;
  210. dataptrdef: tdef;
  211. begin
  212. { nil pointer? }
  213. if not assigned(ll.lab) then
  214. begin
  215. if ll.ofs<>0 then
  216. internalerror(2015030701);
  217. inherited;
  218. exit;
  219. end;
  220. { if the returned offset is <> 0, then the string data
  221. starts at that offset -> translate to a field for the
  222. high level code generator }
  223. if ll.ofs<>0 then
  224. begin
  225. { get the recorddef for this string constant }
  226. if not searchsym_type(ctai_typedconstbuilder.get_dynstring_rec_name(st,winlikewidestring,strlength),srsym,srsymtable) then
  227. internalerror(2014080406);
  228. strrecdef:=trecorddef(ttypesym(srsym).typedef);
  229. { offset in the record of the the string data }
  230. offset:=ctai_typedconstbuilder.get_string_symofs(st,winlikewidestring);
  231. { field corresponding to this offset }
  232. field:=trecordsymtable(strrecdef.symtable).findfieldbyoffset(offset);
  233. { pointerdef to the string data array }
  234. dataptrdef:=getpointerdef(field.vardef);
  235. queue_init(charptrdef);
  236. queue_addrn(dataptrdef,charptrdef);
  237. queue_subscriptn(strrecdef,field);
  238. queue_emit_asmsym(ll.lab,strrecdef);
  239. end
  240. else
  241. { since llvm doesn't support labels in the middle of structs, this
  242. offset should never be 0 }
  243. internalerror(2014080506);
  244. end;
  245. procedure tllvmtai_typedconstbuilder.begin_aggregate_internal(def: tdef; anonymous: boolean);
  246. var
  247. agg: tai_aggregatetypedconst;
  248. tck: ttypedconstkind;
  249. curagg: tllvmaggregateinformation;
  250. begin
  251. tck:=aggregate_kind(def);
  252. if tck<>tck_simple then
  253. begin
  254. { create new typed const aggregate }
  255. agg:=tai_aggregatetypedconst.create(tck,def);
  256. { either add to the current typed const aggregate (if nested), or
  257. emit to the asmlist (if top level) }
  258. curagg:=tllvmaggregateinformation(curagginfo);
  259. if assigned(curagg) then
  260. curagg.aggai.addvalue(agg)
  261. else
  262. fasmlist.concat(agg);
  263. { create aggregate information for this new aggregate }
  264. inherited;
  265. { set new current typed const aggregate }
  266. tllvmaggregateinformation(curagginfo).aggai:=agg
  267. end
  268. else
  269. inherited;
  270. end;
  271. procedure tllvmtai_typedconstbuilder.end_aggregate_internal(def: tdef; anonymous: boolean);
  272. var
  273. info: tllvmaggregateinformation;
  274. begin
  275. if aggregate_kind(def)<>tck_simple then
  276. begin
  277. info:=tllvmaggregateinformation(curagginfo);
  278. if not assigned(info) then
  279. internalerror(2014060101);
  280. info.aggai.finish;
  281. end;
  282. inherited;
  283. end;
  284. procedure tllvmtai_typedconstbuilder.queue_init(todef: tdef);
  285. begin
  286. inherited;
  287. fqueued_tai:=nil;
  288. flast_added_tai:=nil;
  289. fqueued_tai_opidx:=-1;
  290. fqueued_def:=todef;
  291. end;
  292. procedure tllvmtai_typedconstbuilder.queue_vecn(def: tdef; const index: tconstexprint);
  293. var
  294. ai: taillvm;
  295. aityped: tai;
  296. eledef: tdef;
  297. begin
  298. { update range checking info }
  299. inherited;
  300. ai:=taillvm.getelementptr_reg_tai_size_const(NR_NO,nil,ptrsinttype,index.svalue,true);
  301. case def.typ of
  302. arraydef:
  303. eledef:=tarraydef(def).elementdef;
  304. stringdef:
  305. case tstringdef(def).stringtype of
  306. st_shortstring,
  307. st_longstring,
  308. st_ansistring:
  309. eledef:=cansichartype;
  310. st_widestring,
  311. st_unicodestring:
  312. eledef:=cwidechartype;
  313. else
  314. internalerror(2014062202);
  315. end;
  316. else
  317. internalerror(2014062203);
  318. end;
  319. aityped:=wrap_with_type(ai,getpointerdef(eledef));
  320. update_queued_tai(getpointerdef(eledef),aityped,ai,1);
  321. end;
  322. procedure tllvmtai_typedconstbuilder.queue_subscriptn(def: tabstractrecorddef; vs: tfieldvarsym);
  323. var
  324. getllvmfieldaddr,
  325. getpascalfieldaddr,
  326. getllvmfieldaddrtyped: tai;
  327. llvmfielddef: tdef;
  328. begin
  329. { update range checking info }
  330. inherited;
  331. llvmfielddef:=tabstractrecordsymtable(def.symtable).llvmst[vs.llvmfieldnr].def;
  332. { get the address of the llvm-struct field that corresponds to this
  333. Pascal field }
  334. getllvmfieldaddr:=taillvm.getelementptr_reg_tai_size_const(NR_NO,nil,s32inttype,vs.llvmfieldnr,true);
  335. { getelementptr doesn't contain its own resultdef, so encode it via a
  336. tai_simpletypedconst tai }
  337. getllvmfieldaddrtyped:=wrap_with_type(getllvmfieldaddr,getpointerdef(llvmfielddef));
  338. { if it doesn't match the requested field exactly (variant record),
  339. fixup the result }
  340. getpascalfieldaddr:=getllvmfieldaddrtyped;
  341. if (vs.offsetfromllvmfield<>0) or
  342. (llvmfielddef<>vs.vardef) then
  343. begin
  344. { offset of real field relative to llvm-struct field <> 0? }
  345. if vs.offsetfromllvmfield<>0 then
  346. begin
  347. { convert to a pointer to a 1-sized element }
  348. if llvmfielddef.size<>1 then
  349. begin
  350. getpascalfieldaddr:=taillvm.op_reg_tai_size(la_bitcast,NR_NO,getpascalfieldaddr,u8inttype);
  351. { update the current fielddef of the expression }
  352. llvmfielddef:=u8inttype;
  353. end;
  354. { add the offset }
  355. getpascalfieldaddr:=taillvm.getelementptr_reg_tai_size_const(NR_NO,getpascalfieldaddr,ptrsinttype,vs.offsetfromllvmfield,true);
  356. { ... and set the result type of the getelementptr }
  357. getpascalfieldaddr:=wrap_with_type(getpascalfieldaddr,getpointerdef(u8inttype));
  358. llvmfielddef:=u8inttype;
  359. end;
  360. { bitcast the data at the final offset to the right type }
  361. if llvmfielddef<>vs.vardef then
  362. getpascalfieldaddr:=wrap_with_type(taillvm.op_reg_tai_size(la_bitcast,NR_NO,getpascalfieldaddr,getpointerdef(vs.vardef)),getpointerdef(vs.vardef));
  363. end;
  364. update_queued_tai(getpointerdef(vs.vardef),getpascalfieldaddr,getllvmfieldaddr,1);
  365. end;
  366. procedure tllvmtai_typedconstbuilder.queue_typeconvn(fromdef, todef: tdef);
  367. var
  368. ai: taillvm;
  369. typedai: tai;
  370. tmpintdef: tdef;
  371. op,
  372. firstop,
  373. secondop: tllvmop;
  374. begin
  375. inherited;
  376. { special case: procdef -> procvardef/pointerdef: must take address of
  377. the procdef }
  378. if (fromdef.typ=procdef) and
  379. (todef.typ<>procdef) then
  380. fromdef:=tprocdef(fromdef).getcopyas(procvardef,pc_address_only);
  381. op:=llvmconvop(fromdef,todef);
  382. case op of
  383. la_ptrtoint_to_x,
  384. la_x_to_inttoptr:
  385. begin
  386. { convert via an integer with the same size as "x" }
  387. if op=la_ptrtoint_to_x then
  388. begin
  389. tmpintdef:=cgsize_orddef(def_cgsize(todef));
  390. firstop:=la_ptrtoint;
  391. secondop:=la_bitcast
  392. end
  393. else
  394. begin
  395. tmpintdef:=cgsize_orddef(def_cgsize(fromdef));
  396. firstop:=la_bitcast;
  397. secondop:=la_inttoptr;
  398. end;
  399. { since we have to queue operations from outer to inner, first queue
  400. the conversion from the tempintdef to the todef }
  401. ai:=taillvm.op_reg_tai_size(secondop,NR_NO,nil,todef);
  402. typedai:=wrap_with_type(ai,todef);
  403. update_queued_tai(todef,typedai,ai,1);
  404. todef:=tmpintdef;
  405. op:=firstop
  406. end;
  407. end;
  408. ai:=taillvm.op_reg_tai_size(op,NR_NO,nil,todef);
  409. typedai:=wrap_with_type(ai,todef);
  410. update_queued_tai(todef,typedai,ai,1);
  411. end;
  412. procedure tllvmtai_typedconstbuilder.queue_emit_staticvar(vs: tstaticvarsym);
  413. begin
  414. { we've already incorporated the offset via the inserted operations above,
  415. make sure it doesn't get emitted again as part of the tai_const for
  416. the tasmsymbol }
  417. fqueue_offset:=0;
  418. inherited;
  419. end;
  420. procedure tllvmtai_typedconstbuilder.queue_emit_asmsym(sym: tasmsymbol; def: tdef);
  421. begin
  422. { we've already incorporated the offset via the inserted operations above,
  423. make sure it doesn't get emitted again as part of the tai_const for
  424. the tasmsymbol }
  425. fqueue_offset:=0;
  426. inherited;
  427. end;
  428. procedure tllvmtai_typedconstbuilder.queue_emit_ordconst(value: int64; def: tdef);
  429. begin
  430. { no offset into an ordinal constant }
  431. if fqueue_offset<>0 then
  432. internalerror(2015030702);
  433. inherited;
  434. end;
  435. class function tllvmtai_typedconstbuilder.get_string_symofs(typ: tstringtype; winlikewidestring: boolean): pint;
  436. begin
  437. { LLVM does not support labels in the middle of a declaration }
  438. result:=get_string_header_size(typ,winlikewidestring);
  439. end;
  440. begin
  441. ctai_typedconstbuilder:=tllvmtai_typedconstbuilder;
  442. end.