nwasmflw.pas 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  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. procedure generate_exceptreason_throw(reason: tcgint);
  505. var
  506. reasonreg : tregister;
  507. begin
  508. reasonreg:=hlcg.getintregister(current_asmdata.CurrAsmList,exceptionreasontype);
  509. hlcg.g_exception_reason_load(current_asmdata.CurrAsmList,exceptionreasontype,exceptionreasontype,excepttemps.reasonbuf,reasonreg);
  510. thlcgwasm(hlcg).a_cmp_const_reg_stack(current_asmdata.CurrAsmList,exceptionreasontype,OC_EQ,reason,reasonreg);
  511. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
  512. thlcgwasm(hlcg).incblock;
  513. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  514. current_asmdata.CurrAsmList.Concat(taicpu.op_sym(a_throw,current_asmdata.WeakRefAsmSymbol(FPC_EXCEPTION_TAG_SYM,AT_WASM_EXCEPTION_TAG)));
  515. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
  516. thlcgwasm(hlcg).decblock;
  517. end;
  518. begin
  519. location_reset(location,LOC_VOID,OS_NO);
  520. oldBreakLabel:=nil;
  521. oldContinueLabel:=nil;
  522. continuefinallylabel:=nil;
  523. breakfinallylabel:=nil;
  524. oldLoopBreakBr:=0;
  525. oldLoopContBr:=0;
  526. in_loop:=assigned(current_procinfo.CurrBreakLabel);
  527. if not implicitframe then
  528. exceptframekind:=tek_normalfinally
  529. else
  530. exceptframekind:=tek_implicitfinally;
  531. { in 'no exceptions' mode, we still want to handle properly exit,
  532. continue and break (they still need to execute the 'finally'
  533. statements), so for this we need excepttemps.reasonbuf, and for this
  534. reason, we need to allocate excepttemps }
  535. cexceptionstatehandler.get_exception_temps(current_asmdata.CurrAsmList,excepttemps);
  536. cexceptionstatehandler.new_exception(current_asmdata.CurrAsmList,excepttemps,exceptframekind,finallyexceptionstate);
  537. { the finally block must catch break, continue and exit }
  538. { statements }
  539. { the outer 'try..finally' block }
  540. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  541. thlcgwasm(hlcg).incblock;
  542. { the 'exit' block }
  543. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  544. thlcgwasm(hlcg).incblock;
  545. oldCurrExitLabel:=current_procinfo.CurrExitLabel;
  546. oldExitBr:=thlcgwasm(hlcg).exitBr;
  547. exitfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  548. current_procinfo.CurrExitLabel:=exitfinallylabel;
  549. thlcgwasm(hlcg).exitBr:=thlcgwasm(hlcg).br_blocks;
  550. { the 'break' block }
  551. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  552. thlcgwasm(hlcg).incblock;
  553. if in_loop then
  554. begin
  555. oldBreakLabel:=current_procinfo.CurrBreakLabel;
  556. oldLoopBreakBr:=thlcgwasm(hlcg).loopBreakBr;
  557. breakfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  558. current_procinfo.CurrBreakLabel:=breakfinallylabel;
  559. thlcgwasm(hlcg).loopBreakBr:=thlcgwasm(hlcg).br_blocks;
  560. end;
  561. { the 'continue' block }
  562. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  563. thlcgwasm(hlcg).incblock;
  564. if in_loop then
  565. begin
  566. oldContinueLabel:=current_procinfo.CurrContinueLabel;
  567. oldLoopContBr:=thlcgwasm(hlcg).loopContBr;
  568. continuefinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  569. current_procinfo.CurrContinueLabel:=continuefinallylabel;
  570. thlcgwasm(hlcg).loopContBr:=thlcgwasm(hlcg).br_blocks;
  571. end;
  572. { the inner 'try..end_try' block }
  573. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_try));
  574. thlcgwasm(hlcg).incblock;
  575. { try code }
  576. if assigned(left) then
  577. begin
  578. secondpass(left);
  579. if codegenerror then
  580. exit;
  581. end;
  582. { don't generate line info for internal cleanup }
  583. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  584. cexceptionstatehandler.end_try_block(current_asmdata.CurrAsmList,exceptframekind,excepttemps,finallyexceptionstate,nil);
  585. { we've reached the end of the 'try' block, with no exceptions/exit/break/continue, so set exceptionreason:=0 }
  586. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,0,excepttemps.reasonbuf);
  587. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,4)); // jump to the 'finally' section
  588. current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_catch,current_asmdata.WeakRefAsmSymbol(FPC_EXCEPTION_TAG_SYM,AT_WASM_EXCEPTION_TAG)));
  589. { exceptionreason:=1 (exception) }
  590. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,1,excepttemps.reasonbuf);
  591. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,4)); // jump to the 'finally' section
  592. { exit the inner 'try..end_try' block }
  593. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_try));
  594. thlcgwasm(hlcg).decblock;
  595. { exit the 'continue' block }
  596. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  597. thlcgwasm(hlcg).decblock;
  598. { exceptionreason:=4 (continue) }
  599. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,4,excepttemps.reasonbuf);
  600. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,2)); // jump to the 'finally' section
  601. { exit the 'break' block }
  602. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  603. thlcgwasm(hlcg).decblock;
  604. { exceptionreason:=3 (break) }
  605. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,3,excepttemps.reasonbuf);
  606. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,1)); // jump to the 'finally' section
  607. { exit the 'exit' block }
  608. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  609. thlcgwasm(hlcg).decblock;
  610. { exceptionreason:=2 (exit) }
  611. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,2,excepttemps.reasonbuf);
  612. { proceed to the 'finally' section, which follow immediately, no need for jumps }
  613. { exit the outer 'try..finally' block }
  614. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  615. thlcgwasm(hlcg).decblock;
  616. { end cleanup }
  617. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  618. { finally code (don't unconditionally set fc_inflowcontrol, since the
  619. finally code is unconditionally executed; we do have to filter out
  620. flags regarding break/contrinue/etc. because we have to give an
  621. error in case one of those is used in the finally-code }
  622. flowcontrol:=finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions];
  623. secondpass(right);
  624. { goto is allowed if it stays inside the finally block,
  625. this is checked using the exception block number }
  626. if (flowcontrol-[fc_gotolabel])<>(finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions]) then
  627. CGMessage(cg_e_control_flow_outside_finally);
  628. if codegenerror then
  629. exit;
  630. { don't generate line info for internal cleanup }
  631. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  632. if fc_exit in finallyexceptionstate.newflowcontrol then
  633. generate_exceptreason_check_br(2,thlcgwasm(hlcg).br_blocks-oldExitBr);
  634. if fc_break in finallyexceptionstate.newflowcontrol then
  635. generate_exceptreason_check_br(3,thlcgwasm(hlcg).br_blocks-oldLoopBreakBr);
  636. if fc_continue in finallyexceptionstate.newflowcontrol then
  637. generate_exceptreason_check_br(4,thlcgwasm(hlcg).br_blocks-oldLoopContBr);
  638. generate_exceptreason_throw(1);
  639. cexceptionstatehandler.unget_exception_temps(current_asmdata.CurrAsmList,excepttemps);
  640. { end cleanup }
  641. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  642. current_procinfo.CurrExitLabel:=oldCurrExitLabel;
  643. thlcgwasm(hlcg).exitBr:=oldExitBr;
  644. if assigned(current_procinfo.CurrBreakLabel) then
  645. begin
  646. current_procinfo.CurrContinueLabel:=oldContinueLabel;
  647. thlcgwasm(hlcg).loopContBr:=oldLoopContBr;
  648. current_procinfo.CurrBreakLabel:=oldBreakLabel;
  649. thlcgwasm(hlcg).loopBreakBr:=oldLoopBreakBr;
  650. end;
  651. flowcontrol:=finallyexceptionstate.oldflowcontrol+(finallyexceptionstate.newflowcontrol-[fc_inflowcontrol,fc_catching_exceptions]);
  652. end;
  653. procedure twasmtryfinallynode.pass_generate_code;
  654. begin
  655. if ts_wasm_no_exceptions in current_settings.targetswitches then
  656. pass_generate_code_no_exceptions
  657. else if ts_wasm_js_exceptions in current_settings.targetswitches then
  658. pass_generate_code_js_exceptions
  659. else if ts_wasm_native_exceptions in current_settings.targetswitches then
  660. pass_generate_code_native_exceptions
  661. else
  662. internalerror(2021091704);
  663. end;
  664. initialization
  665. cifnode:=twasmifnode;
  666. cwhilerepeatnode:=twasmwhilerepeatnode;
  667. craisenode:=twasmraisenode;
  668. ctryexceptnode:=twasmtryexceptnode;
  669. ctryfinallynode:=twasmtryfinallynode;
  670. end.