nllvmflw.pas 20 KB

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