llvmpi.pas 20 KB

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