llvmpi.pas 20 KB

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