llvmpi.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. {
  2. Copyright (c) 2016 by Jonas Maebe
  3. Information about the current procedure that is being compiled
  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 llvmpi;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. cclasses,
  22. aasmbase,
  23. procinfo,
  24. cpupi,
  25. aasmdata,aasmllvm;
  26. type
  27. tllvmprocinfo = class(tcpuprocinfo)
  28. private
  29. fexceptlabelstack: tfplist;
  30. flandingpadstack: tfplist;
  31. public
  32. constructor create(aparent: tprocinfo); override;
  33. destructor destroy; override;
  34. procedure pushexceptlabel(lab: TAsmLabel);
  35. { returns true if there no more landing pads on the stack }
  36. function popexceptlabel(lab: TAsmLabel): boolean;
  37. function CurrExceptLabel: TAsmLabel;
  38. procedure pushlandingpad(pad: taillvm);
  39. procedure poppad;
  40. function currlandingpad: taillvm;
  41. procedure setup_eh; override;
  42. procedure finish_eh; override;
  43. procedure start_eh(list: TAsmList); override;
  44. procedure end_eh(list: TAsmList); override;
  45. end;
  46. implementation
  47. uses
  48. globtype,globals,verbose,systems,
  49. symconst,symtype,symdef,symsym,symtable,defutil,llvmdef,
  50. pass_2,
  51. parabase,paramgr,
  52. cgbase,cgutils,cgexcept,tgobj,hlcgobj,llvmbase;
  53. {*****************************************************************************
  54. tllvmexceptionstatehandler
  55. *****************************************************************************}
  56. type
  57. tllvmexceptionstatehandler = class(tcgexceptionstatehandler)
  58. class procedure get_exception_temps(list: TAsmList; var t: texceptiontemps); override;
  59. class procedure unget_exception_temps(list: TAsmList; const t: texceptiontemps); override;
  60. class procedure new_exception(list: TAsmList; const t: texceptiontemps; const exceptframekind: texceptframekind; out exceptstate: texceptionstate); override;
  61. class procedure emit_except_label(list: TAsmList; exceptframekind: texceptframekind; var exceptionstate: texceptionstate;var exceptiontemps:texceptiontemps); override;
  62. class procedure end_try_block(list: TAsmList; exceptframekind: texceptframekind; const t: texceptiontemps; var exceptionstate: texceptionstate; endlabel: TAsmLabel); override;
  63. class procedure cleanupobjectstack(list: TAsmList); override;
  64. class procedure popaddrstack(list: TAsmList); override;
  65. class procedure handle_reraise(list: TAsmList; const t: texceptiontemps; const entrystate: texceptionstate; const exceptframekind: texceptframekind); override;
  66. class procedure begin_catch(list: TAsmList; excepttype: tobjectdef; nextonlabel: tasmlabel; out exceptlocdef: tdef; out exceptlocreg: tregister); override;
  67. class procedure end_catch(list: TAsmList); override;
  68. class procedure catch_all_start(list: TAsmList); override;
  69. class procedure catch_all_end(list: TAsmList); override;
  70. protected
  71. class procedure begin_catch_internal(list: TAsmList; excepttype: tobjectdef; nextonlabel: tasmlabel; add_catch: boolean; out exceptlocdef: tdef; out exceptlocreg: tregister);
  72. class procedure catch_all_start_internal(list: TAsmList; add_catch: boolean);
  73. end;
  74. class procedure tllvmexceptionstatehandler.get_exception_temps(list: TAsmList; var t: texceptiontemps);
  75. begin
  76. tg.gethltemp(list,ossinttype,ossinttype.size,tt_persistent,t.reasonbuf);
  77. end;
  78. class procedure tllvmexceptionstatehandler.unget_exception_temps(list: TAsmList; const t: texceptiontemps);
  79. begin
  80. tg.ungettemp(list,t.reasonbuf);
  81. tllvmprocinfo(current_procinfo).poppad;
  82. end;
  83. class procedure tllvmexceptionstatehandler.new_exception(list: TAsmList; const t: texceptiontemps; const exceptframekind: texceptframekind; out exceptstate: texceptionstate);
  84. var
  85. reg: tregister;
  86. begin
  87. exceptstate.oldflowcontrol:=flowcontrol;
  88. if exceptframekind<>tek_except then
  89. current_asmdata.getjumplabel(exceptstate.finallycodelabel)
  90. else
  91. exceptstate.finallycodelabel:=nil;
  92. { all calls inside the exception block have to be invokes instead,
  93. which refer to the exception label:
  94. exceptionlabel:
  95. %reg = landingpad ..
  96. <exception handling code>
  97. }
  98. current_asmdata.getjumplabel(exceptstate.exceptionlabel);
  99. { for consistency checking when popping }
  100. tllvmprocinfo(current_procinfo).pushexceptlabel(exceptstate.exceptionlabel);
  101. flowcontrol:=[fc_inflowcontrol,fc_catching_exceptions];
  102. { the reasonbuf is set to 1 by the generic code if we got in
  103. the exception block by catching an exception -> do the same here, so
  104. we can share that generic code; llvm will optimise it away. The
  105. reasonbuf is later also used for break/continue/... }
  106. reg:=hlcg.getintregister(list,ossinttype);
  107. hlcg.a_load_const_reg(list,ossinttype,1,reg);
  108. hlcg.g_exception_reason_save(list,ossinttype,ossinttype,reg,t.reasonbuf);
  109. { There can only be a landingpad if there were any invokes in the try-block,
  110. as otherwise we get an error; we can also generate exceptions from
  111. invalid memory accesses and the like, but LLVM cannot model that
  112. --
  113. We cheat for now by adding an invoke to a dummy routine at the start and at
  114. the end of the try-block. That will not magically fix the state
  115. of all variables when the exception gets caught though. }
  116. hlcg.g_call_system_proc(list,'FPC_DUMMYPOTENTIALRAISE',[],nil).resetiftemp;
  117. end;
  118. class procedure tllvmexceptionstatehandler.emit_except_label(list: TAsmList; exceptframekind: texceptframekind; var exceptionstate: texceptionstate;var exceptiontemps:texceptiontemps);
  119. var
  120. reg: tregister;
  121. landingpad: taillvm;
  122. landingpaddef: trecorddef;
  123. begin
  124. hlcg.g_unreachable(list);
  125. hlcg.a_label(list,exceptionstate.exceptionlabel);
  126. { use packrecords 1 because we don't want padding (LLVM 4.0+ requires
  127. exactly two fields in this struct) }
  128. landingpaddef:=llvmgettemprecorddef([voidpointertype,u32inttype],
  129. 1,
  130. targetinfos[target_info.system]^.alignment.recordalignmin,
  131. targetinfos[target_info.system]^.alignment.maxCrecordalign);
  132. reg:=hlcg.getregisterfordef(list,landingpaddef);
  133. landingpad:=taillvm.landingpad(reg,landingpaddef,{clause}nil);
  134. list.concat(landingpad);
  135. if exceptframekind<>tek_except then
  136. begin
  137. if not assigned(exceptionstate.finallycodelabel) then
  138. internalerror(2018111102);
  139. if use_cleanup(exceptframekind) then
  140. landingpad.landingpad_add_clause(la_cleanup, nil, nil)
  141. else
  142. landingpad.landingpad_add_clause(la_catch, voidpointertype, nil);
  143. hlcg.a_label(list,exceptionstate.finallycodelabel);
  144. exceptionstate.finallycodelabel:=nil;
  145. end;
  146. { consistency check }
  147. tllvmprocinfo(current_procinfo).popexceptlabel(exceptionstate.exceptionlabel);
  148. tllvmprocinfo(current_procinfo).pushlandingpad(landingpad);
  149. end;
  150. class procedure tllvmexceptionstatehandler.end_try_block(list: TAsmList; exceptframekind: texceptframekind; const t: texceptiontemps; var exceptionstate: texceptionstate; endlabel: TAsmLabel);
  151. var
  152. reg: tregister;
  153. begin
  154. { llvm does not allow creating a landing pad if there are no invokes in
  155. the try block -> create a call to a dummy routine that cannot be
  156. analysed by llvm and that supposedly may raise an exception. Has to
  157. be combined with marking stores inside try blocks as volatile and the
  158. loads afterwards as well in order to guarantee correct optimizations
  159. in case an exception gets triggered inside a try-block though }
  160. hlcg.g_call_system_proc(list,'FPC_DUMMYPOTENTIALRAISE',[],nil).resetiftemp;
  161. { record that no exception happened in the reason buf }
  162. reg:=hlcg.getintregister(list,ossinttype);
  163. hlcg.a_load_const_reg(list,ossinttype,0,reg);
  164. hlcg.g_exception_reason_save(list,ossinttype,ossinttype,reg,t.reasonbuf);
  165. inherited;
  166. if exceptframekind=tek_except then
  167. hlcg.a_jmp_always(list,endlabel);
  168. end;
  169. class procedure tllvmexceptionstatehandler.cleanupobjectstack(list: TAsmList);
  170. var
  171. landingpad: taillvm;
  172. begin
  173. { if not a single catch block added -> catch all }
  174. landingpad:=tllvmprocinfo(current_procinfo).currlandingpad;
  175. if assigned(landingpad) and
  176. not assigned(landingpad.oper[2]^.ai) then
  177. begin
  178. landingpad.landingpad_add_clause(la_catch,voidpointertype,nil);
  179. end;
  180. end;
  181. class procedure tllvmexceptionstatehandler.popaddrstack(list: TAsmList);
  182. begin
  183. // nothing
  184. end;
  185. class procedure tllvmexceptionstatehandler.handle_reraise(list: TAsmList; const t: texceptiontemps; const entrystate: texceptionstate; const exceptframekind: texceptframekind);
  186. var
  187. landingpad: taillvm;
  188. landingpadres: tregister;
  189. landingpadresdef: tdef;
  190. begin
  191. { We use resume to propagate the exception to an outer function frame, and call
  192. reraise in case we are nested in another exception frame in the current function
  193. (because then we will emit an invoke which will tie this re-raise to that other
  194. exception frame; that is impossible to do with a resume instruction).
  195. Furthermore, the resume opcode only works for landingpads with a cleanup clause,
  196. which we only generate for outer implicitfinally frames }
  197. if not(fc_catching_exceptions in flowcontrol) and
  198. use_cleanup(exceptframekind) then
  199. begin
  200. { resume <result from catchpad> }
  201. landingpad:=tllvmprocinfo(current_procinfo).currlandingpad;
  202. landingpadres:=landingpad.oper[0]^.reg;
  203. landingpadresdef:=landingpad.oper[1]^.def;
  204. list.concat(taillvm.op_size_reg(la_resume,landingpadresdef,landingpadres));
  205. end
  206. else
  207. begin
  208. { Need a begin_catch so that the reraise will know what exception to throw.
  209. Don't need to add a "catch all" to the landing pad, as it contains one.
  210. We want to rethrow whatever exception was caught rather than guarantee
  211. that all possible kinds of exceptions get caught. }
  212. catch_all_start_internal(list,false);
  213. hlcg.g_call_system_proc(list,'fpc_reraise',[],nil).resetiftemp;
  214. end;
  215. end;
  216. class procedure tllvmexceptionstatehandler.begin_catch(list: TAsmList; excepttype: tobjectdef; nextonlabel: tasmlabel; out exceptlocdef: tdef; out exceptlocreg: tregister);
  217. begin
  218. begin_catch_internal(list,excepttype,nextonlabel,true,exceptlocdef,exceptlocreg);
  219. end;
  220. class procedure tllvmexceptionstatehandler.end_catch(list: TAsmList);
  221. begin
  222. hlcg.g_call_system_proc(list,'fpc_psabi_end_catch',[],nil).resetiftemp;
  223. inherited;
  224. end;
  225. class procedure tllvmexceptionstatehandler.catch_all_start(list: TAsmList);
  226. begin
  227. catch_all_start_internal(list,true);
  228. end;
  229. class procedure tllvmexceptionstatehandler.catch_all_end(list: TAsmList);
  230. begin
  231. hlcg.g_call_system_proc(list,'fpc_psabi_end_catch',[],nil).resetiftemp;
  232. end;
  233. class procedure tllvmexceptionstatehandler.begin_catch_internal(list: TAsmList; excepttype: tobjectdef; nextonlabel: tasmlabel; add_catch: boolean; out exceptlocdef: tdef; out exceptlocreg: tregister);
  234. var
  235. catchstartlab: tasmlabel;
  236. landingpad: taillvm;
  237. begincatchres,
  238. typeidres,
  239. paraloc1: tcgpara;
  240. pd: tprocdef;
  241. landingpadstructdef,
  242. landingpadtypeiddef: tdef;
  243. rttisym: TAsmSymbol;
  244. rttidef: tdef;
  245. rttiref: treference;
  246. wrappedexception,
  247. exceptiontypeidreg,
  248. landingpadres: tregister;
  249. exceptloc: tlocation;
  250. indirect: boolean;
  251. otherunit: boolean;
  252. begin
  253. paraloc1.init;
  254. landingpad:=tllvmprocinfo(current_procinfo).currlandingpad;
  255. rttidef:=nil;
  256. rttisym:=nil;
  257. if add_catch then
  258. begin
  259. if assigned(excepttype) then
  260. begin
  261. otherunit:=findunitsymtable(excepttype.owner).moduleid<>findunitsymtable(current_procinfo.procdef.owner).moduleid;
  262. indirect:=(tf_supports_packages in target_info.flags) and
  263. (target_info.system in systems_indirect_var_imports) and
  264. (cs_imported_data in current_settings.localswitches) and
  265. otherunit;
  266. { add "catch exceptiontype" clause to the landing pad }
  267. rttidef:=cpointerdef.getreusable(excepttype.vmt_def);
  268. rttisym:=current_asmdata.RefAsmSymbol(excepttype.vmt_mangledname, AT_DATA, indirect);
  269. landingpad.landingpad_add_clause(la_catch,rttidef,rttisym);
  270. end
  271. else
  272. begin
  273. landingpad.landingpad_add_clause(la_catch,voidpointertype,nil);
  274. end;
  275. end;
  276. { pascal_exception := FPC_psabi_begin_catch(wrappedExceptionObject) where
  277. wrappedExceptionObject is the exception returned by the landingpad }
  278. landingpadres:=landingpad.oper[0]^.reg;
  279. landingpadstructdef:=landingpad.oper[1]^.def;
  280. { check if the exception is handled by this node }
  281. if assigned(excepttype) then
  282. begin
  283. landingpadtypeiddef:=tfieldvarsym(trecorddef(landingpadstructdef).symtable.symlist[1]).vardef;
  284. exceptiontypeidreg:=hlcg.getaddressregister(list,landingpadtypeiddef);
  285. pd:=search_system_proc('llvm_eh_typeid_for');
  286. paramanager.getintparaloc(list,pd,1,paraloc1);
  287. reference_reset_symbol(rttiref,rttisym,0,rttidef.alignment,[]);
  288. rttiref.refaddr:=addr_full;
  289. hlcg.a_load_ref_cgpara(list,cpointerdef.getreusable(rttidef),rttiref,paraloc1);
  290. typeidres:=hlcg.g_call_system_proc(list,pd,[@paraloc1],nil);
  291. location_reset(exceptloc, LOC_REGISTER, def_cgsize(landingpadtypeiddef));
  292. exceptloc.register:=hlcg.getintregister(list,landingpadtypeiddef);
  293. hlcg.gen_load_cgpara_loc(list, landingpadtypeiddef, typeidres, exceptloc, true);
  294. list.concat(taillvm.extract(la_extractvalue,exceptiontypeidreg,landingpadstructdef,landingpadres,1));
  295. current_asmdata.getjumplabel(catchstartlab);
  296. hlcg.a_cmp_reg_loc_label(list,typeidres.Def,OC_EQ,exceptiontypeidreg,exceptloc,catchstartlab);
  297. hlcg.a_jmp_always(list,nextonlabel);
  298. hlcg.a_label(list,catchstartlab);
  299. typeidres.resetiftemp;
  300. end;
  301. wrappedexception:=hlcg.getaddressregister(list,voidpointertype);
  302. list.concat(taillvm.extract(la_extractvalue,wrappedexception,landingpadstructdef,landingpadres,0));
  303. pd:=search_system_proc('fpc_psabi_begin_catch');
  304. paramanager.getintparaloc(list, pd, 1, paraloc1);
  305. hlcg.a_load_reg_cgpara(list,voidpointertype,wrappedexception,paraloc1);
  306. begincatchres:=hlcg.g_call_system_proc(list,pd,[@paraloc1],nil);
  307. location_reset(exceptloc, LOC_REGISTER, def_cgsize(begincatchres.def));
  308. exceptloc.register:=hlcg.getaddressregister(list, begincatchres.def);
  309. hlcg.gen_load_cgpara_loc(list, begincatchres.def, begincatchres, exceptloc, true);
  310. begincatchres.resetiftemp;
  311. paraloc1.done;
  312. exceptlocdef:=begincatchres.def;
  313. exceptlocreg:=exceptloc.register;
  314. end;
  315. class procedure tllvmexceptionstatehandler.catch_all_start_internal(list: TAsmList; add_catch: boolean);
  316. var
  317. exceptlocdef: tdef;
  318. exceptlocreg: tregister;
  319. begin
  320. begin_catch_internal(list,nil,nil,add_catch,exceptlocdef,exceptlocreg);
  321. end;
  322. {*****************************************************************************
  323. tllvmprocinfo
  324. *****************************************************************************}
  325. constructor tllvmprocinfo.create(aparent: tprocinfo);
  326. begin
  327. inherited;
  328. fexceptlabelstack:=tfplist.create;
  329. flandingpadstack:=tfplist.create;
  330. end;
  331. destructor tllvmprocinfo.destroy;
  332. begin
  333. if fexceptlabelstack.Count<>0 then
  334. Internalerror(2016121301);
  335. fexceptlabelstack.free;
  336. if flandingpadstack.Count<>0 then
  337. internalerror(2018051901);
  338. flandingpadstack.free;
  339. inherited;
  340. end;
  341. procedure tllvmprocinfo.pushexceptlabel(lab: TAsmLabel);
  342. begin
  343. fexceptlabelstack.add(lab);
  344. end;
  345. function tllvmprocinfo.popexceptlabel(lab: TAsmLabel): boolean;
  346. begin
  347. if CurrExceptLabel<>lab then
  348. internalerror(2016121302);
  349. fexceptlabelstack.count:=fexceptlabelstack.count-1;
  350. result:=fexceptlabelstack.count=0;
  351. end;
  352. function tllvmprocinfo.CurrExceptLabel: TAsmLabel; inline;
  353. begin
  354. result:=TAsmLabel(fexceptlabelstack.last);
  355. if not assigned(result) then
  356. internalerror(2016121703);
  357. end;
  358. procedure tllvmprocinfo.pushlandingpad(pad: taillvm);
  359. begin
  360. flandingpadstack.add(pad);
  361. end;
  362. procedure tllvmprocinfo.poppad;
  363. begin
  364. if flandingpadstack.Count=0 then
  365. internalerror(2018051902);
  366. flandingpadstack.Count:=flandingpadstack.Count-1;
  367. end;
  368. function tllvmprocinfo.currlandingpad: taillvm;
  369. begin
  370. if flandingpadstack.Count=0 then
  371. internalerror(2018051903);
  372. result:=taillvm(flandingpadstack.last);
  373. end;
  374. procedure tllvmprocinfo.setup_eh;
  375. begin
  376. if po_assembler in procdef.procoptions then
  377. inherited
  378. else
  379. begin
  380. cexceptionstatehandler:=tllvmexceptionstatehandler;
  381. end;
  382. end;
  383. procedure tllvmprocinfo.finish_eh;
  384. begin
  385. if po_assembler in procdef.procoptions then
  386. inherited;
  387. end;
  388. procedure tllvmprocinfo.start_eh(list: TAsmList);
  389. begin
  390. if po_assembler in procdef.procoptions then
  391. inherited;
  392. end;
  393. procedure tllvmprocinfo.end_eh(list: TAsmList);
  394. begin
  395. if po_assembler in procdef.procoptions then
  396. inherited;
  397. end;
  398. begin
  399. if not assigned(cprocinfo) then
  400. begin
  401. writeln('Internalerror 2018052005');
  402. halt(1);
  403. end;
  404. cprocinfo:=tllvmprocinfo;
  405. end.