nwasmflw.pas 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  1. {
  2. Copyright (c) 2019 by Dmitry Boyarintsev
  3. Generate assembler for nodes that influence the flow for the JVM
  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 nwasmflw;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. aasmbase,node,nflw,ncgflw, cutils;
  22. type
  23. { twasmifnode }
  24. { Wasm doesn't have any jump(+offset) operations
  25. It only provide structured blockes to handle jumps
  26. (It's possible to jump-out-of-block at any time)
  27. "If" is also implemented as a block, identical to high-level language. }
  28. twasmifnode = class(tcgifnode)
  29. public
  30. procedure pass_generate_code;override;
  31. end;
  32. { twasmwhilerepeatnode }
  33. twasmwhilerepeatnode = class(tcgwhilerepeatnode)
  34. public
  35. procedure pass_generate_code_condition;
  36. procedure pass_generate_code;override;
  37. end;
  38. { twasmraisenode }
  39. twasmraisenode = class(tcgraisenode)
  40. private
  41. function pass_1_no_exceptions : tnode;
  42. function pass_1_native_exceptions : tnode;
  43. public
  44. function pass_1 : tnode;override;
  45. end;
  46. { twasmtryexceptnode }
  47. twasmtryexceptnode = class(tcgtryexceptnode)
  48. private
  49. procedure pass_generate_code_no_exceptions;
  50. procedure pass_generate_code_js_exceptions;
  51. procedure pass_generate_code_native_exceptions;
  52. public
  53. procedure pass_generate_code;override;
  54. end;
  55. { twasmtryfinallynode }
  56. twasmtryfinallynode = class(tcgtryfinallynode)
  57. private
  58. procedure pass_generate_code_no_exceptions;
  59. procedure pass_generate_code_js_exceptions;
  60. procedure pass_generate_code_native_exceptions;
  61. public
  62. procedure pass_generate_code;override;
  63. end;
  64. implementation
  65. uses
  66. verbose,globals,systems,globtype,constexp,
  67. symconst,symdef,symsym,aasmtai,aasmdata,aasmcpu,defutil,defcmp,
  68. procinfo,cgbase,cgexcept,pass_1,pass_2,parabase,compinnr,
  69. cpubase,cpuinfo,
  70. nbas,nld,ncon,ncnv,ncal,ninl,nmem,nadd,
  71. tgobj,paramgr,
  72. cgutils,hlcgobj,hlcgcpu;
  73. {*****************************************************************************
  74. twasmwhilerepeatnode
  75. *****************************************************************************}
  76. procedure twasmwhilerepeatnode.pass_generate_code_condition;
  77. begin
  78. secondpass(left);
  79. thlcgwasm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,left.resultdef,left.location);
  80. // reversing the condition
  81. if not (lnf_checknegate in loopflags) then
  82. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_i32_eqz));
  83. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br_if,1) );
  84. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  85. end;
  86. procedure twasmwhilerepeatnode.pass_generate_code;
  87. var
  88. lcont,lbreak,lloop,
  89. oldclabel,oldblabel : tasmlabel;
  90. truelabel,falselabel : tasmlabel;
  91. oldflowcontrol : tflowcontrol;
  92. oldloopcontbroffset: Integer;
  93. oldloopbreakbroffset: Integer;
  94. begin
  95. location_reset(location,LOC_VOID,OS_NO);
  96. current_asmdata.getjumplabel(lloop);
  97. current_asmdata.getjumplabel(lcont);
  98. current_asmdata.getjumplabel(lbreak);
  99. oldflowcontrol:=flowcontrol;
  100. oldloopcontbroffset:=thlcgwasm(hlcg).loopContBr;
  101. oldloopbreakbroffset:=thlcgwasm(hlcg).loopBreakBr;
  102. oldclabel:=current_procinfo.CurrContinueLabel;
  103. oldblabel:=current_procinfo.CurrBreakLabel;
  104. include(flowcontrol,fc_inflowcontrol);
  105. exclude(flowcontrol,fc_unwind_loop);
  106. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  107. thlcgwasm(hlcg).incblock;
  108. thlcgwasm(hlcg).loopBreakBr:=thlcgwasm(hlcg).br_blocks;
  109. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_loop));
  110. thlcgwasm(hlcg).incblock;
  111. if lnf_testatbegin in loopflags then
  112. begin
  113. pass_generate_code_condition;
  114. thlcgwasm(hlcg).loopContBr:=thlcgwasm(hlcg).br_blocks;
  115. end else
  116. thlcgwasm(hlcg).loopContBr:=thlcgwasm(hlcg).br_blocks+1;
  117. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  118. thlcgwasm(hlcg).incblock;
  119. current_procinfo.CurrContinueLabel:=lcont;
  120. current_procinfo.CurrBreakLabel:=lbreak;
  121. secondpass(right);
  122. if (lnf_testatbegin in loopflags) then
  123. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,1) ); // jump back to the external loop
  124. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  125. thlcgwasm(hlcg).decblock;
  126. if not (lnf_testatbegin in loopflags) then begin
  127. pass_generate_code_condition;
  128. end;
  129. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,0) ); // jump back to loop
  130. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_loop));
  131. thlcgwasm(hlcg).decblock;
  132. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  133. thlcgwasm(hlcg).decblock;
  134. current_procinfo.CurrContinueLabel:=oldclabel;
  135. current_procinfo.CurrBreakLabel:=oldblabel;
  136. thlcgwasm(hlcg).loopContBr:=oldloopcontbroffset;
  137. thlcgwasm(hlcg).loopBreakBr:=oldloopbreakbroffset;
  138. { a break/continue in a while/repeat block can't be seen outside }
  139. flowcontrol:=oldflowcontrol+(flowcontrol-[fc_break,fc_continue,fc_inflowcontrol]);
  140. end;
  141. {*****************************************************************************
  142. twasmifnode
  143. *****************************************************************************}
  144. procedure twasmifnode.pass_generate_code;
  145. var
  146. oldflowcontrol: tflowcontrol;
  147. begin
  148. // left - condition
  149. // right - then
  150. // t1 - else (optional)
  151. location_reset(location,LOC_VOID,OS_NO);
  152. oldflowcontrol := flowcontrol;
  153. include(flowcontrol,fc_inflowcontrol);
  154. //todo: MOVE all current_asm_data actions to Wasm HL CodeGen
  155. secondpass(left); // condition exprssions
  156. thlcgwasm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,left.resultdef,left.location);
  157. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
  158. thlcgwasm(hlcg).incblock;
  159. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  160. if Assigned(right) then
  161. secondpass(right); // then branchs
  162. if Assigned(t1) then // else branch
  163. begin
  164. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_else));
  165. secondpass(t1);
  166. end;
  167. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
  168. thlcgwasm(hlcg).decblock;
  169. flowcontrol := oldflowcontrol + (flowcontrol - [fc_inflowcontrol]);
  170. end;
  171. {*****************************************************************************
  172. twasmraisenode
  173. *****************************************************************************}
  174. function twasmraisenode.pass_1_no_exceptions : tnode;
  175. var
  176. statements : tstatementnode;
  177. //current_addr : tlabelnode;
  178. raisenode : tcallnode;
  179. begin
  180. result:=internalstatements(statements);
  181. if assigned(left) then
  182. begin
  183. { first para must be a class }
  184. firstpass(left);
  185. { insert needed typeconvs for addr,frame }
  186. if assigned(right) then
  187. begin
  188. { addr }
  189. firstpass(right);
  190. { frame }
  191. if assigned(third) then
  192. firstpass(third)
  193. else
  194. third:=cpointerconstnode.Create(0,voidpointertype);
  195. end
  196. else
  197. begin
  198. third:=cinlinenode.create(in_get_frame,false,nil);
  199. //current_addr:=clabelnode.create(cnothingnode.create,clabelsym.create('$raiseaddr'));
  200. //addstatement(statements,current_addr);
  201. //right:=caddrnode.create(cloadnode.create(current_addr.labsym,current_addr.labsym.owner));
  202. right:=cnilnode.create;
  203. { raise address off by one so we are for sure inside the action area for the raise }
  204. if tf_use_psabieh in target_info.flags then
  205. right:=caddnode.create_internal(addn,right,cordconstnode.create(1,sizesinttype,false));
  206. end;
  207. raisenode:=ccallnode.createintern('fpc_raiseexception',
  208. ccallparanode.create(third,
  209. ccallparanode.create(right,
  210. ccallparanode.create(left,nil)))
  211. );
  212. include(raisenode.callnodeflags,cnf_call_never_returns);
  213. addstatement(statements,raisenode);
  214. end
  215. else
  216. begin
  217. addstatement(statements,ccallnode.createintern('fpc_popaddrstack',nil));
  218. raisenode:=ccallnode.createintern('fpc_reraise',nil);
  219. include(raisenode.callnodeflags,cnf_call_never_returns);
  220. addstatement(statements,raisenode);
  221. end;
  222. left:=nil;
  223. right:=nil;
  224. third:=nil;
  225. end;
  226. function twasmraisenode.pass_1_native_exceptions : tnode;
  227. var
  228. statements : tstatementnode;
  229. //current_addr : tlabelnode;
  230. raisenode : tcallnode;
  231. begin
  232. result:=internalstatements(statements);
  233. if assigned(left) then
  234. begin
  235. { first para must be a class }
  236. firstpass(left);
  237. { insert needed typeconvs for addr,frame }
  238. if assigned(right) then
  239. begin
  240. { addr }
  241. firstpass(right);
  242. { frame }
  243. if assigned(third) then
  244. firstpass(third)
  245. else
  246. third:=cpointerconstnode.Create(0,voidpointertype);
  247. end
  248. else
  249. begin
  250. third:=cinlinenode.create(in_get_frame,false,nil);
  251. //current_addr:=clabelnode.create(cnothingnode.create,clabelsym.create('$raiseaddr'));
  252. //addstatement(statements,current_addr);
  253. //right:=caddrnode.create(cloadnode.create(current_addr.labsym,current_addr.labsym.owner));
  254. right:=cnilnode.create;
  255. { raise address off by one so we are for sure inside the action area for the raise }
  256. if tf_use_psabieh in target_info.flags then
  257. right:=caddnode.create_internal(addn,right,cordconstnode.create(1,sizesinttype,false));
  258. end;
  259. raisenode:=ccallnode.createintern('fpc_raiseexception',
  260. ccallparanode.create(third,
  261. ccallparanode.create(right,
  262. ccallparanode.create(left,nil)))
  263. );
  264. include(raisenode.callnodeflags,cnf_call_never_returns);
  265. addstatement(statements,raisenode);
  266. end
  267. else
  268. begin
  269. addstatement(statements,ccallnode.createintern('fpc_popaddrstack',nil));
  270. raisenode:=ccallnode.createintern('fpc_reraise',nil);
  271. include(raisenode.callnodeflags,cnf_call_never_returns);
  272. addstatement(statements,raisenode);
  273. end;
  274. left:=nil;
  275. right:=nil;
  276. third:=nil;
  277. end;
  278. function twasmraisenode.pass_1 : tnode;
  279. begin
  280. if ts_wasm_no_exceptions in current_settings.targetswitches then
  281. result:=pass_1_no_exceptions
  282. else if ts_wasm_native_exceptions in current_settings.targetswitches then
  283. result:=pass_1_native_exceptions
  284. else
  285. result:=inherited;
  286. end;
  287. {*****************************************************************************
  288. twasmtryexceptnode
  289. *****************************************************************************}
  290. procedure twasmtryexceptnode.pass_generate_code_no_exceptions;
  291. begin
  292. location_reset(location,LOC_VOID,OS_NO);
  293. secondpass(left);
  294. end;
  295. procedure twasmtryexceptnode.pass_generate_code_js_exceptions;
  296. begin
  297. internalerror(2021091706);
  298. end;
  299. procedure twasmtryexceptnode.pass_generate_code_native_exceptions;
  300. begin
  301. location_reset(location,LOC_VOID,OS_NO);
  302. secondpass(left);
  303. end;
  304. procedure twasmtryexceptnode.pass_generate_code;
  305. begin
  306. if ts_wasm_no_exceptions in current_settings.targetswitches then
  307. pass_generate_code_no_exceptions
  308. else if ts_wasm_js_exceptions in current_settings.targetswitches then
  309. pass_generate_code_js_exceptions
  310. else if ts_wasm_native_exceptions in current_settings.targetswitches then
  311. pass_generate_code_native_exceptions
  312. else
  313. internalerror(2021091705);
  314. end;
  315. {*****************************************************************************
  316. twasmtryfinallynode
  317. *****************************************************************************}
  318. procedure twasmtryfinallynode.pass_generate_code_no_exceptions;
  319. var
  320. exitfinallylabel,
  321. continuefinallylabel,
  322. breakfinallylabel,
  323. oldCurrExitLabel,
  324. oldContinueLabel,
  325. oldBreakLabel: tasmlabel;
  326. oldLoopContBr: integer;
  327. oldLoopBreakBr: integer;
  328. oldExitBr: integer;
  329. finallyexceptionstate: tcgexceptionstatehandler.texceptionstate;
  330. excepttemps : tcgexceptionstatehandler.texceptiontemps;
  331. exceptframekind: tcgexceptionstatehandler.texceptframekind;
  332. in_loop: Boolean;
  333. procedure generate_exceptreason_check_br(reason: tcgint; br: aint);
  334. var
  335. reasonreg : tregister;
  336. begin
  337. reasonreg:=hlcg.getintregister(current_asmdata.CurrAsmList,exceptionreasontype);
  338. hlcg.g_exception_reason_load(current_asmdata.CurrAsmList,exceptionreasontype,exceptionreasontype,excepttemps.reasonbuf,reasonreg);
  339. thlcgwasm(hlcg).a_cmp_const_reg_stack(current_asmdata.CurrAsmList,exceptionreasontype,OC_EQ,reason,reasonreg);
  340. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
  341. thlcgwasm(hlcg).incblock;
  342. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  343. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,br+1));
  344. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
  345. thlcgwasm(hlcg).decblock;
  346. end;
  347. begin
  348. location_reset(location,LOC_VOID,OS_NO);
  349. oldBreakLabel:=nil;
  350. oldContinueLabel:=nil;
  351. continuefinallylabel:=nil;
  352. breakfinallylabel:=nil;
  353. oldLoopBreakBr:=0;
  354. oldLoopContBr:=0;
  355. in_loop:=assigned(current_procinfo.CurrBreakLabel);
  356. if not implicitframe then
  357. exceptframekind:=tek_normalfinally
  358. else
  359. exceptframekind:=tek_implicitfinally;
  360. { in 'no exceptions' mode, we still want to handle properly exit,
  361. continue and break (they still need to execute the 'finally'
  362. statements), so for this we need excepttemps.reasonbuf, and for this
  363. reason, we need to allocate excepttemps }
  364. cexceptionstatehandler.get_exception_temps(current_asmdata.CurrAsmList,excepttemps);
  365. cexceptionstatehandler.new_exception(current_asmdata.CurrAsmList,excepttemps,exceptframekind,finallyexceptionstate);
  366. { the finally block must catch break, continue and exit }
  367. { statements }
  368. { the outer 'try..finally' block }
  369. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  370. thlcgwasm(hlcg).incblock;
  371. { the 'exit' block }
  372. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  373. thlcgwasm(hlcg).incblock;
  374. oldCurrExitLabel:=current_procinfo.CurrExitLabel;
  375. oldExitBr:=thlcgwasm(hlcg).exitBr;
  376. exitfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  377. current_procinfo.CurrExitLabel:=exitfinallylabel;
  378. thlcgwasm(hlcg).exitBr:=thlcgwasm(hlcg).br_blocks;
  379. { the 'break' block }
  380. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  381. thlcgwasm(hlcg).incblock;
  382. if in_loop then
  383. begin
  384. oldBreakLabel:=current_procinfo.CurrBreakLabel;
  385. oldLoopBreakBr:=thlcgwasm(hlcg).loopBreakBr;
  386. breakfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  387. current_procinfo.CurrBreakLabel:=breakfinallylabel;
  388. thlcgwasm(hlcg).loopBreakBr:=thlcgwasm(hlcg).br_blocks;
  389. end;
  390. { the 'continue' block }
  391. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  392. thlcgwasm(hlcg).incblock;
  393. if in_loop then
  394. begin
  395. oldContinueLabel:=current_procinfo.CurrContinueLabel;
  396. oldLoopContBr:=thlcgwasm(hlcg).loopContBr;
  397. continuefinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  398. current_procinfo.CurrContinueLabel:=continuefinallylabel;
  399. thlcgwasm(hlcg).loopContBr:=thlcgwasm(hlcg).br_blocks;
  400. end;
  401. { try code }
  402. if assigned(left) then
  403. begin
  404. secondpass(left);
  405. if codegenerror then
  406. exit;
  407. end;
  408. { don't generate line info for internal cleanup }
  409. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  410. cexceptionstatehandler.end_try_block(current_asmdata.CurrAsmList,exceptframekind,excepttemps,finallyexceptionstate,nil);
  411. { we've reached the end of the 'try' block, with no exceptions/exit/break/continue, so set exceptionreason:=0 }
  412. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,0,excepttemps.reasonbuf);
  413. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,3)); // jump to the 'finally' section
  414. { exit the 'continue' block }
  415. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  416. thlcgwasm(hlcg).decblock;
  417. { exceptionreason:=4 (continue) }
  418. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,4,excepttemps.reasonbuf);
  419. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,2)); // jump to the 'finally' section
  420. { exit the 'break' block }
  421. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  422. thlcgwasm(hlcg).decblock;
  423. { exceptionreason:=3 (break) }
  424. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,3,excepttemps.reasonbuf);
  425. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,1)); // jump to the 'finally' section
  426. { exit the 'exit' block }
  427. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  428. thlcgwasm(hlcg).decblock;
  429. { exceptionreason:=2 (exit) }
  430. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,2,excepttemps.reasonbuf);
  431. { proceed to the 'finally' section, which follow immediately, no need for jumps }
  432. { exit the outer 'try..finally' block }
  433. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  434. thlcgwasm(hlcg).decblock;
  435. { end cleanup }
  436. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  437. { finally code (don't unconditionally set fc_inflowcontrol, since the
  438. finally code is unconditionally executed; we do have to filter out
  439. flags regarding break/contrinue/etc. because we have to give an
  440. error in case one of those is used in the finally-code }
  441. flowcontrol:=finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions];
  442. secondpass(right);
  443. { goto is allowed if it stays inside the finally block,
  444. this is checked using the exception block number }
  445. if (flowcontrol-[fc_gotolabel])<>(finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions]) then
  446. CGMessage(cg_e_control_flow_outside_finally);
  447. if codegenerror then
  448. exit;
  449. { don't generate line info for internal cleanup }
  450. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  451. if fc_exit in finallyexceptionstate.newflowcontrol then
  452. generate_exceptreason_check_br(2,thlcgwasm(hlcg).br_blocks-oldExitBr);
  453. if fc_break in finallyexceptionstate.newflowcontrol then
  454. generate_exceptreason_check_br(3,thlcgwasm(hlcg).br_blocks-oldLoopBreakBr);
  455. if fc_continue in finallyexceptionstate.newflowcontrol then
  456. generate_exceptreason_check_br(4,thlcgwasm(hlcg).br_blocks-oldLoopContBr);
  457. cexceptionstatehandler.unget_exception_temps(current_asmdata.CurrAsmList,excepttemps);
  458. { end cleanup }
  459. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  460. current_procinfo.CurrExitLabel:=oldCurrExitLabel;
  461. thlcgwasm(hlcg).exitBr:=oldExitBr;
  462. if assigned(current_procinfo.CurrBreakLabel) then
  463. begin
  464. current_procinfo.CurrContinueLabel:=oldContinueLabel;
  465. thlcgwasm(hlcg).loopContBr:=oldLoopContBr;
  466. current_procinfo.CurrBreakLabel:=oldBreakLabel;
  467. thlcgwasm(hlcg).loopBreakBr:=oldLoopBreakBr;
  468. end;
  469. flowcontrol:=finallyexceptionstate.oldflowcontrol+(finallyexceptionstate.newflowcontrol-[fc_inflowcontrol,fc_catching_exceptions]);
  470. end;
  471. procedure twasmtryfinallynode.pass_generate_code_js_exceptions;
  472. begin
  473. internalerror(2021091702);
  474. end;
  475. procedure twasmtryfinallynode.pass_generate_code_native_exceptions;
  476. var
  477. exitfinallylabel,
  478. continuefinallylabel,
  479. breakfinallylabel,
  480. oldCurrExitLabel,
  481. oldContinueLabel,
  482. oldBreakLabel: tasmlabel;
  483. oldLoopContBr: integer;
  484. oldLoopBreakBr: integer;
  485. oldExitBr: integer;
  486. finallyexceptionstate: tcgexceptionstatehandler.texceptionstate;
  487. excepttemps : tcgexceptionstatehandler.texceptiontemps;
  488. exceptframekind: tcgexceptionstatehandler.texceptframekind;
  489. in_loop: Boolean;
  490. procedure generate_exceptreason_check_br(reason: tcgint; br: aint);
  491. var
  492. reasonreg : tregister;
  493. begin
  494. reasonreg:=hlcg.getintregister(current_asmdata.CurrAsmList,exceptionreasontype);
  495. hlcg.g_exception_reason_load(current_asmdata.CurrAsmList,exceptionreasontype,exceptionreasontype,excepttemps.reasonbuf,reasonreg);
  496. thlcgwasm(hlcg).a_cmp_const_reg_stack(current_asmdata.CurrAsmList,exceptionreasontype,OC_EQ,reason,reasonreg);
  497. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
  498. thlcgwasm(hlcg).incblock;
  499. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  500. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,br+1));
  501. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
  502. thlcgwasm(hlcg).decblock;
  503. end;
  504. begin
  505. location_reset(location,LOC_VOID,OS_NO);
  506. oldBreakLabel:=nil;
  507. oldContinueLabel:=nil;
  508. continuefinallylabel:=nil;
  509. breakfinallylabel:=nil;
  510. oldLoopBreakBr:=0;
  511. oldLoopContBr:=0;
  512. in_loop:=assigned(current_procinfo.CurrBreakLabel);
  513. if not implicitframe then
  514. exceptframekind:=tek_normalfinally
  515. else
  516. exceptframekind:=tek_implicitfinally;
  517. { in 'no exceptions' mode, we still want to handle properly exit,
  518. continue and break (they still need to execute the 'finally'
  519. statements), so for this we need excepttemps.reasonbuf, and for this
  520. reason, we need to allocate excepttemps }
  521. cexceptionstatehandler.get_exception_temps(current_asmdata.CurrAsmList,excepttemps);
  522. cexceptionstatehandler.new_exception(current_asmdata.CurrAsmList,excepttemps,exceptframekind,finallyexceptionstate);
  523. { the finally block must catch break, continue and exit }
  524. { statements }
  525. { the outer 'try..finally' block }
  526. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  527. thlcgwasm(hlcg).incblock;
  528. { the 'exit' block }
  529. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  530. thlcgwasm(hlcg).incblock;
  531. oldCurrExitLabel:=current_procinfo.CurrExitLabel;
  532. oldExitBr:=thlcgwasm(hlcg).exitBr;
  533. exitfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  534. current_procinfo.CurrExitLabel:=exitfinallylabel;
  535. thlcgwasm(hlcg).exitBr:=thlcgwasm(hlcg).br_blocks;
  536. { the 'break' block }
  537. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  538. thlcgwasm(hlcg).incblock;
  539. if in_loop then
  540. begin
  541. oldBreakLabel:=current_procinfo.CurrBreakLabel;
  542. oldLoopBreakBr:=thlcgwasm(hlcg).loopBreakBr;
  543. breakfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  544. current_procinfo.CurrBreakLabel:=breakfinallylabel;
  545. thlcgwasm(hlcg).loopBreakBr:=thlcgwasm(hlcg).br_blocks;
  546. end;
  547. { the 'continue' block }
  548. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  549. thlcgwasm(hlcg).incblock;
  550. if in_loop then
  551. begin
  552. oldContinueLabel:=current_procinfo.CurrContinueLabel;
  553. oldLoopContBr:=thlcgwasm(hlcg).loopContBr;
  554. continuefinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  555. current_procinfo.CurrContinueLabel:=continuefinallylabel;
  556. thlcgwasm(hlcg).loopContBr:=thlcgwasm(hlcg).br_blocks;
  557. end;
  558. { the inner 'try..end_try' block }
  559. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_try));
  560. thlcgwasm(hlcg).incblock;
  561. { try code }
  562. if assigned(left) then
  563. begin
  564. secondpass(left);
  565. if codegenerror then
  566. exit;
  567. end;
  568. { don't generate line info for internal cleanup }
  569. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  570. cexceptionstatehandler.end_try_block(current_asmdata.CurrAsmList,exceptframekind,excepttemps,finallyexceptionstate,nil);
  571. { we've reached the end of the 'try' block, with no exceptions/exit/break/continue, so set exceptionreason:=0 }
  572. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,0,excepttemps.reasonbuf);
  573. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,4)); // jump to the 'finally' section
  574. current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_catch,current_asmdata.WeakRefAsmSymbol(FPC_EXCEPTION_TAG_SYM,AT_WASM_EXCEPTION_TAG)));
  575. thlcgwasm(hlcg).decblock;
  576. { exceptionreason:=1 (exception) }
  577. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,1,excepttemps.reasonbuf);
  578. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,3)); // jump to the 'finally' section
  579. { exit the inner 'try..end_try' block }
  580. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_try));
  581. { exit the 'continue' block }
  582. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  583. thlcgwasm(hlcg).decblock;
  584. { exceptionreason:=4 (continue) }
  585. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,4,excepttemps.reasonbuf);
  586. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,2)); // jump to the 'finally' section
  587. { exit the 'break' block }
  588. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  589. thlcgwasm(hlcg).decblock;
  590. { exceptionreason:=3 (break) }
  591. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,3,excepttemps.reasonbuf);
  592. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,1)); // jump to the 'finally' section
  593. { exit the 'exit' block }
  594. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  595. thlcgwasm(hlcg).decblock;
  596. { exceptionreason:=2 (exit) }
  597. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,2,excepttemps.reasonbuf);
  598. { proceed to the 'finally' section, which follow immediately, no need for jumps }
  599. { exit the outer 'try..finally' block }
  600. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  601. thlcgwasm(hlcg).decblock;
  602. { end cleanup }
  603. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  604. { finally code (don't unconditionally set fc_inflowcontrol, since the
  605. finally code is unconditionally executed; we do have to filter out
  606. flags regarding break/contrinue/etc. because we have to give an
  607. error in case one of those is used in the finally-code }
  608. flowcontrol:=finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions];
  609. secondpass(right);
  610. { goto is allowed if it stays inside the finally block,
  611. this is checked using the exception block number }
  612. if (flowcontrol-[fc_gotolabel])<>(finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions]) then
  613. CGMessage(cg_e_control_flow_outside_finally);
  614. if codegenerror then
  615. exit;
  616. { don't generate line info for internal cleanup }
  617. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  618. if fc_exit in finallyexceptionstate.newflowcontrol then
  619. generate_exceptreason_check_br(2,thlcgwasm(hlcg).br_blocks-oldExitBr);
  620. if fc_break in finallyexceptionstate.newflowcontrol then
  621. generate_exceptreason_check_br(3,thlcgwasm(hlcg).br_blocks-oldLoopBreakBr);
  622. if fc_continue in finallyexceptionstate.newflowcontrol then
  623. generate_exceptreason_check_br(4,thlcgwasm(hlcg).br_blocks-oldLoopContBr);
  624. cexceptionstatehandler.unget_exception_temps(current_asmdata.CurrAsmList,excepttemps);
  625. { end cleanup }
  626. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  627. current_procinfo.CurrExitLabel:=oldCurrExitLabel;
  628. thlcgwasm(hlcg).exitBr:=oldExitBr;
  629. if assigned(current_procinfo.CurrBreakLabel) then
  630. begin
  631. current_procinfo.CurrContinueLabel:=oldContinueLabel;
  632. thlcgwasm(hlcg).loopContBr:=oldLoopContBr;
  633. current_procinfo.CurrBreakLabel:=oldBreakLabel;
  634. thlcgwasm(hlcg).loopBreakBr:=oldLoopBreakBr;
  635. end;
  636. flowcontrol:=finallyexceptionstate.oldflowcontrol+(finallyexceptionstate.newflowcontrol-[fc_inflowcontrol,fc_catching_exceptions]);
  637. end;
  638. procedure twasmtryfinallynode.pass_generate_code;
  639. begin
  640. if ts_wasm_no_exceptions in current_settings.targetswitches then
  641. pass_generate_code_no_exceptions
  642. else if ts_wasm_js_exceptions in current_settings.targetswitches then
  643. pass_generate_code_js_exceptions
  644. else if ts_wasm_native_exceptions in current_settings.targetswitches then
  645. pass_generate_code_native_exceptions
  646. else
  647. internalerror(2021091704);
  648. end;
  649. initialization
  650. cifnode:=twasmifnode;
  651. cwhilerepeatnode:=twasmwhilerepeatnode;
  652. craisenode:=twasmraisenode;
  653. ctryexceptnode:=twasmtryexceptnode;
  654. ctryfinallynode:=twasmtryfinallynode;
  655. end.