nwasmflw.pas 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905
  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,nutils,
  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. var
  301. trystate,doobjectdestroyandreraisestate: tcgexceptionstatehandler.texceptionstate;
  302. destroytemps,
  303. excepttemps: tcgexceptionstatehandler.texceptiontemps;
  304. afteronflowcontrol: tflowcontrol;
  305. label
  306. errorexit;
  307. begin
  308. location_reset(location,LOC_VOID,OS_NO);
  309. { Exception temps? We don't need no stinking exception temps! :) }
  310. fillchar(excepttemps,sizeof(excepttemps),0);
  311. reference_reset(excepttemps.envbuf,0,[]);
  312. reference_reset(excepttemps.jmpbuf,0,[]);
  313. reference_reset(excepttemps.reasonbuf,0,[]);
  314. //exceptstate.oldflowcontrol:=flowcontrol;
  315. //flowcontrol:=[fc_inflowcontrol,fc_catching_exceptions];
  316. cexceptionstatehandler.new_exception(current_asmdata.CurrAsmList,excepttemps,tek_except,trystate);
  317. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_try));
  318. thlcgwasm(hlcg).incblock;
  319. { try block }
  320. secondpass(left);
  321. if codegenerror then
  322. goto errorexit;
  323. //exceptionstate.newflowcontrol:=flowcontrol;
  324. //flowcontrol:=exceptionstate.oldflowcontrol;
  325. cexceptionstatehandler.end_try_block(current_asmdata.CurrAsmList,tek_except,excepttemps,trystate,nil);
  326. current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_catch,current_asmdata.WeakRefAsmSymbol(FPC_EXCEPTION_TAG_SYM,AT_WASM_EXCEPTION_TAG)));
  327. flowcontrol:=[fc_inflowcontrol]+trystate.oldflowcontrol*[fc_catching_exceptions];
  328. { on statements }
  329. //if assigned(right) then
  330. // secondpass(right);
  331. afteronflowcontrol:=flowcontrol;
  332. { default handling except handling }
  333. if assigned(t1) then
  334. begin
  335. { FPC_CATCHES with 'default handler' flag (=-1) need no longer be called,
  336. it doesn't change any state and its return value is ignored (Sergei)
  337. }
  338. { the destruction of the exception object must be also }
  339. { guarded by an exception frame, but it can be omitted }
  340. { if there's no user code in 'except' block }
  341. if not (has_no_code(t1)) then
  342. begin
  343. { if there is an outer frame that catches exceptions, remember this for the "except"
  344. part of this try/except }
  345. flowcontrol:=trystate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions];
  346. { Exception temps? We don't need no stinking exception temps! :) }
  347. fillchar(excepttemps,sizeof(destroytemps),0);
  348. reference_reset(destroytemps.envbuf,0,[]);
  349. reference_reset(destroytemps.jmpbuf,0,[]);
  350. reference_reset(destroytemps.reasonbuf,0,[]);
  351. cexceptionstatehandler.new_exception(current_asmdata.CurrAsmList,destroytemps,tek_except,doobjectdestroyandreraisestate);
  352. { the flowcontrol from the default except-block must be merged
  353. with the flowcontrol flags potentially set by the
  354. on-statements handled above (secondpass(right)), as they are
  355. at the same program level }
  356. flowcontrol:=
  357. flowcontrol+
  358. afteronflowcontrol;
  359. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_try));
  360. thlcgwasm(hlcg).incblock;
  361. secondpass(t1);
  362. hlcg.g_call_system_proc(current_asmdata.CurrAsmList,'fpc_doneexception',[],nil).resetiftemp;
  363. current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_catch,current_asmdata.WeakRefAsmSymbol(FPC_EXCEPTION_TAG_SYM,AT_WASM_EXCEPTION_TAG)));
  364. hlcg.g_call_system_proc(current_asmdata.CurrAsmList,'fpc_raise_nested',[],nil).resetiftemp;
  365. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_try));
  366. end
  367. else
  368. begin
  369. doobjectdestroyandreraisestate.newflowcontrol:=afteronflowcontrol;
  370. hlcg.g_call_system_proc(current_asmdata.CurrAsmList,'fpc_doneexception',[],nil).resetiftemp;
  371. end;
  372. end
  373. else
  374. begin
  375. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_rethrow,0));
  376. doobjectdestroyandreraisestate.newflowcontrol:=afteronflowcontrol;
  377. end;
  378. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_try));
  379. thlcgwasm(hlcg).decblock;
  380. errorexit:
  381. { return all used control flow statements }
  382. flowcontrol:=trystate.oldflowcontrol+(doobjectdestroyandreraisestate.newflowcontrol +
  383. trystate.newflowcontrol - [fc_inflowcontrol,fc_catching_exceptions]);
  384. end;
  385. procedure twasmtryexceptnode.pass_generate_code;
  386. begin
  387. if ts_wasm_no_exceptions in current_settings.targetswitches then
  388. pass_generate_code_no_exceptions
  389. else if ts_wasm_js_exceptions in current_settings.targetswitches then
  390. pass_generate_code_js_exceptions
  391. else if ts_wasm_native_exceptions in current_settings.targetswitches then
  392. pass_generate_code_native_exceptions
  393. else
  394. internalerror(2021091705);
  395. end;
  396. {*****************************************************************************
  397. twasmtryfinallynode
  398. *****************************************************************************}
  399. procedure twasmtryfinallynode.pass_generate_code_no_exceptions;
  400. var
  401. exitfinallylabel,
  402. continuefinallylabel,
  403. breakfinallylabel,
  404. oldCurrExitLabel,
  405. oldContinueLabel,
  406. oldBreakLabel: tasmlabel;
  407. oldLoopContBr: integer;
  408. oldLoopBreakBr: integer;
  409. oldExitBr: integer;
  410. finallyexceptionstate: tcgexceptionstatehandler.texceptionstate;
  411. excepttemps : tcgexceptionstatehandler.texceptiontemps;
  412. exceptframekind: tcgexceptionstatehandler.texceptframekind;
  413. in_loop: Boolean;
  414. procedure generate_exceptreason_check_br(reason: tcgint; br: aint);
  415. var
  416. reasonreg : tregister;
  417. begin
  418. reasonreg:=hlcg.getintregister(current_asmdata.CurrAsmList,exceptionreasontype);
  419. hlcg.g_exception_reason_load(current_asmdata.CurrAsmList,exceptionreasontype,exceptionreasontype,excepttemps.reasonbuf,reasonreg);
  420. thlcgwasm(hlcg).a_cmp_const_reg_stack(current_asmdata.CurrAsmList,exceptionreasontype,OC_EQ,reason,reasonreg);
  421. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
  422. thlcgwasm(hlcg).incblock;
  423. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  424. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,br+1));
  425. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
  426. thlcgwasm(hlcg).decblock;
  427. end;
  428. begin
  429. location_reset(location,LOC_VOID,OS_NO);
  430. oldBreakLabel:=nil;
  431. oldContinueLabel:=nil;
  432. continuefinallylabel:=nil;
  433. breakfinallylabel:=nil;
  434. oldLoopBreakBr:=0;
  435. oldLoopContBr:=0;
  436. in_loop:=assigned(current_procinfo.CurrBreakLabel);
  437. if not implicitframe then
  438. exceptframekind:=tek_normalfinally
  439. else
  440. exceptframekind:=tek_implicitfinally;
  441. { in 'no exceptions' mode, we still want to handle properly exit,
  442. continue and break (they still need to execute the 'finally'
  443. statements), so for this we need excepttemps.reasonbuf, and for this
  444. reason, we need to allocate excepttemps }
  445. cexceptionstatehandler.get_exception_temps(current_asmdata.CurrAsmList,excepttemps);
  446. cexceptionstatehandler.new_exception(current_asmdata.CurrAsmList,excepttemps,exceptframekind,finallyexceptionstate);
  447. { the finally block must catch break, continue and exit }
  448. { statements }
  449. { the outer 'try..finally' block }
  450. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  451. thlcgwasm(hlcg).incblock;
  452. { the 'exit' block }
  453. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  454. thlcgwasm(hlcg).incblock;
  455. oldCurrExitLabel:=current_procinfo.CurrExitLabel;
  456. oldExitBr:=thlcgwasm(hlcg).exitBr;
  457. exitfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  458. current_procinfo.CurrExitLabel:=exitfinallylabel;
  459. thlcgwasm(hlcg).exitBr:=thlcgwasm(hlcg).br_blocks;
  460. { the 'break' block }
  461. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  462. thlcgwasm(hlcg).incblock;
  463. if in_loop then
  464. begin
  465. oldBreakLabel:=current_procinfo.CurrBreakLabel;
  466. oldLoopBreakBr:=thlcgwasm(hlcg).loopBreakBr;
  467. breakfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  468. current_procinfo.CurrBreakLabel:=breakfinallylabel;
  469. thlcgwasm(hlcg).loopBreakBr:=thlcgwasm(hlcg).br_blocks;
  470. end;
  471. { the 'continue' block }
  472. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  473. thlcgwasm(hlcg).incblock;
  474. if in_loop then
  475. begin
  476. oldContinueLabel:=current_procinfo.CurrContinueLabel;
  477. oldLoopContBr:=thlcgwasm(hlcg).loopContBr;
  478. continuefinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  479. current_procinfo.CurrContinueLabel:=continuefinallylabel;
  480. thlcgwasm(hlcg).loopContBr:=thlcgwasm(hlcg).br_blocks;
  481. end;
  482. { try code }
  483. if assigned(left) then
  484. begin
  485. secondpass(left);
  486. if codegenerror then
  487. exit;
  488. end;
  489. { don't generate line info for internal cleanup }
  490. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  491. cexceptionstatehandler.end_try_block(current_asmdata.CurrAsmList,exceptframekind,excepttemps,finallyexceptionstate,nil);
  492. { we've reached the end of the 'try' block, with no exceptions/exit/break/continue, so set exceptionreason:=0 }
  493. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,0,excepttemps.reasonbuf);
  494. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,3)); // jump to the 'finally' section
  495. { exit the 'continue' block }
  496. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  497. thlcgwasm(hlcg).decblock;
  498. { exceptionreason:=4 (continue) }
  499. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,4,excepttemps.reasonbuf);
  500. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,2)); // jump to the 'finally' section
  501. { exit the 'break' block }
  502. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  503. thlcgwasm(hlcg).decblock;
  504. { exceptionreason:=3 (break) }
  505. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,3,excepttemps.reasonbuf);
  506. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,1)); // jump to the 'finally' section
  507. { exit the 'exit' block }
  508. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  509. thlcgwasm(hlcg).decblock;
  510. { exceptionreason:=2 (exit) }
  511. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,2,excepttemps.reasonbuf);
  512. { proceed to the 'finally' section, which follow immediately, no need for jumps }
  513. { exit the outer 'try..finally' block }
  514. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  515. thlcgwasm(hlcg).decblock;
  516. { end cleanup }
  517. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  518. { finally code (don't unconditionally set fc_inflowcontrol, since the
  519. finally code is unconditionally executed; we do have to filter out
  520. flags regarding break/contrinue/etc. because we have to give an
  521. error in case one of those is used in the finally-code }
  522. flowcontrol:=finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions];
  523. secondpass(right);
  524. { goto is allowed if it stays inside the finally block,
  525. this is checked using the exception block number }
  526. if (flowcontrol-[fc_gotolabel])<>(finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions]) then
  527. CGMessage(cg_e_control_flow_outside_finally);
  528. if codegenerror then
  529. exit;
  530. { don't generate line info for internal cleanup }
  531. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  532. if fc_exit in finallyexceptionstate.newflowcontrol then
  533. generate_exceptreason_check_br(2,thlcgwasm(hlcg).br_blocks-oldExitBr);
  534. if fc_break in finallyexceptionstate.newflowcontrol then
  535. generate_exceptreason_check_br(3,thlcgwasm(hlcg).br_blocks-oldLoopBreakBr);
  536. if fc_continue in finallyexceptionstate.newflowcontrol then
  537. generate_exceptreason_check_br(4,thlcgwasm(hlcg).br_blocks-oldLoopContBr);
  538. cexceptionstatehandler.unget_exception_temps(current_asmdata.CurrAsmList,excepttemps);
  539. { end cleanup }
  540. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  541. current_procinfo.CurrExitLabel:=oldCurrExitLabel;
  542. thlcgwasm(hlcg).exitBr:=oldExitBr;
  543. if assigned(current_procinfo.CurrBreakLabel) then
  544. begin
  545. current_procinfo.CurrContinueLabel:=oldContinueLabel;
  546. thlcgwasm(hlcg).loopContBr:=oldLoopContBr;
  547. current_procinfo.CurrBreakLabel:=oldBreakLabel;
  548. thlcgwasm(hlcg).loopBreakBr:=oldLoopBreakBr;
  549. end;
  550. flowcontrol:=finallyexceptionstate.oldflowcontrol+(finallyexceptionstate.newflowcontrol-[fc_inflowcontrol,fc_catching_exceptions]);
  551. end;
  552. procedure twasmtryfinallynode.pass_generate_code_js_exceptions;
  553. begin
  554. internalerror(2021091702);
  555. end;
  556. procedure twasmtryfinallynode.pass_generate_code_native_exceptions;
  557. var
  558. exitfinallylabel,
  559. continuefinallylabel,
  560. breakfinallylabel,
  561. oldCurrExitLabel,
  562. oldContinueLabel,
  563. oldBreakLabel: tasmlabel;
  564. oldLoopContBr: integer;
  565. oldLoopBreakBr: integer;
  566. oldExitBr: integer;
  567. finallyexceptionstate: tcgexceptionstatehandler.texceptionstate;
  568. excepttemps : tcgexceptionstatehandler.texceptiontemps;
  569. exceptframekind: tcgexceptionstatehandler.texceptframekind;
  570. in_loop: Boolean;
  571. procedure generate_exceptreason_check_br(reason: tcgint; br: aint);
  572. var
  573. reasonreg : tregister;
  574. begin
  575. reasonreg:=hlcg.getintregister(current_asmdata.CurrAsmList,exceptionreasontype);
  576. hlcg.g_exception_reason_load(current_asmdata.CurrAsmList,exceptionreasontype,exceptionreasontype,excepttemps.reasonbuf,reasonreg);
  577. thlcgwasm(hlcg).a_cmp_const_reg_stack(current_asmdata.CurrAsmList,exceptionreasontype,OC_EQ,reason,reasonreg);
  578. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
  579. thlcgwasm(hlcg).incblock;
  580. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  581. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,br+1));
  582. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
  583. thlcgwasm(hlcg).decblock;
  584. end;
  585. procedure generate_exceptreason_throw(reason: tcgint);
  586. var
  587. reasonreg : tregister;
  588. begin
  589. reasonreg:=hlcg.getintregister(current_asmdata.CurrAsmList,exceptionreasontype);
  590. hlcg.g_exception_reason_load(current_asmdata.CurrAsmList,exceptionreasontype,exceptionreasontype,excepttemps.reasonbuf,reasonreg);
  591. thlcgwasm(hlcg).a_cmp_const_reg_stack(current_asmdata.CurrAsmList,exceptionreasontype,OC_EQ,reason,reasonreg);
  592. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
  593. thlcgwasm(hlcg).incblock;
  594. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  595. current_asmdata.CurrAsmList.Concat(taicpu.op_sym(a_throw,current_asmdata.WeakRefAsmSymbol(FPC_EXCEPTION_TAG_SYM,AT_WASM_EXCEPTION_TAG)));
  596. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
  597. thlcgwasm(hlcg).decblock;
  598. end;
  599. begin
  600. location_reset(location,LOC_VOID,OS_NO);
  601. oldBreakLabel:=nil;
  602. oldContinueLabel:=nil;
  603. continuefinallylabel:=nil;
  604. breakfinallylabel:=nil;
  605. oldLoopBreakBr:=0;
  606. oldLoopContBr:=0;
  607. in_loop:=assigned(current_procinfo.CurrBreakLabel);
  608. if not implicitframe then
  609. exceptframekind:=tek_normalfinally
  610. else
  611. exceptframekind:=tek_implicitfinally;
  612. { in 'no exceptions' mode, we still want to handle properly exit,
  613. continue and break (they still need to execute the 'finally'
  614. statements), so for this we need excepttemps.reasonbuf, and for this
  615. reason, we need to allocate excepttemps }
  616. cexceptionstatehandler.get_exception_temps(current_asmdata.CurrAsmList,excepttemps);
  617. cexceptionstatehandler.new_exception(current_asmdata.CurrAsmList,excepttemps,exceptframekind,finallyexceptionstate);
  618. { the finally block must catch break, continue and exit }
  619. { statements }
  620. { the outer 'try..finally' block }
  621. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  622. thlcgwasm(hlcg).incblock;
  623. { the 'exit' block }
  624. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  625. thlcgwasm(hlcg).incblock;
  626. oldCurrExitLabel:=current_procinfo.CurrExitLabel;
  627. oldExitBr:=thlcgwasm(hlcg).exitBr;
  628. exitfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  629. current_procinfo.CurrExitLabel:=exitfinallylabel;
  630. thlcgwasm(hlcg).exitBr:=thlcgwasm(hlcg).br_blocks;
  631. { the 'break' block }
  632. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  633. thlcgwasm(hlcg).incblock;
  634. if in_loop then
  635. begin
  636. oldBreakLabel:=current_procinfo.CurrBreakLabel;
  637. oldLoopBreakBr:=thlcgwasm(hlcg).loopBreakBr;
  638. breakfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  639. current_procinfo.CurrBreakLabel:=breakfinallylabel;
  640. thlcgwasm(hlcg).loopBreakBr:=thlcgwasm(hlcg).br_blocks;
  641. end;
  642. { the 'continue' block }
  643. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  644. thlcgwasm(hlcg).incblock;
  645. if in_loop then
  646. begin
  647. oldContinueLabel:=current_procinfo.CurrContinueLabel;
  648. oldLoopContBr:=thlcgwasm(hlcg).loopContBr;
  649. continuefinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  650. current_procinfo.CurrContinueLabel:=continuefinallylabel;
  651. thlcgwasm(hlcg).loopContBr:=thlcgwasm(hlcg).br_blocks;
  652. end;
  653. { the inner 'try..end_try' block }
  654. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_try));
  655. thlcgwasm(hlcg).incblock;
  656. { try code }
  657. if assigned(left) then
  658. begin
  659. secondpass(left);
  660. if codegenerror then
  661. exit;
  662. end;
  663. { don't generate line info for internal cleanup }
  664. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  665. cexceptionstatehandler.end_try_block(current_asmdata.CurrAsmList,exceptframekind,excepttemps,finallyexceptionstate,nil);
  666. { we've reached the end of the 'try' block, with no exceptions/exit/break/continue, so set exceptionreason:=0 }
  667. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,0,excepttemps.reasonbuf);
  668. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,4)); // jump to the 'finally' section
  669. current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_catch,current_asmdata.WeakRefAsmSymbol(FPC_EXCEPTION_TAG_SYM,AT_WASM_EXCEPTION_TAG)));
  670. { exceptionreason:=1 (exception) }
  671. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,1,excepttemps.reasonbuf);
  672. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,4)); // jump to the 'finally' section
  673. { exit the inner 'try..end_try' block }
  674. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_try));
  675. thlcgwasm(hlcg).decblock;
  676. { exit the 'continue' block }
  677. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  678. thlcgwasm(hlcg).decblock;
  679. { exceptionreason:=4 (continue) }
  680. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,4,excepttemps.reasonbuf);
  681. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,2)); // jump to the 'finally' section
  682. { exit the 'break' block }
  683. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  684. thlcgwasm(hlcg).decblock;
  685. { exceptionreason:=3 (break) }
  686. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,3,excepttemps.reasonbuf);
  687. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,1)); // jump to the 'finally' section
  688. { exit the 'exit' block }
  689. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  690. thlcgwasm(hlcg).decblock;
  691. { exceptionreason:=2 (exit) }
  692. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,2,excepttemps.reasonbuf);
  693. { proceed to the 'finally' section, which follow immediately, no need for jumps }
  694. { exit the outer 'try..finally' block }
  695. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  696. thlcgwasm(hlcg).decblock;
  697. { end cleanup }
  698. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  699. { finally code (don't unconditionally set fc_inflowcontrol, since the
  700. finally code is unconditionally executed; we do have to filter out
  701. flags regarding break/contrinue/etc. because we have to give an
  702. error in case one of those is used in the finally-code }
  703. flowcontrol:=finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions];
  704. secondpass(right);
  705. { goto is allowed if it stays inside the finally block,
  706. this is checked using the exception block number }
  707. if (flowcontrol-[fc_gotolabel])<>(finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions]) then
  708. CGMessage(cg_e_control_flow_outside_finally);
  709. if codegenerror then
  710. exit;
  711. { don't generate line info for internal cleanup }
  712. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  713. if fc_exit in finallyexceptionstate.newflowcontrol then
  714. generate_exceptreason_check_br(2,thlcgwasm(hlcg).br_blocks-oldExitBr);
  715. if fc_break in finallyexceptionstate.newflowcontrol then
  716. generate_exceptreason_check_br(3,thlcgwasm(hlcg).br_blocks-oldLoopBreakBr);
  717. if fc_continue in finallyexceptionstate.newflowcontrol then
  718. generate_exceptreason_check_br(4,thlcgwasm(hlcg).br_blocks-oldLoopContBr);
  719. generate_exceptreason_throw(1);
  720. cexceptionstatehandler.unget_exception_temps(current_asmdata.CurrAsmList,excepttemps);
  721. { end cleanup }
  722. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  723. current_procinfo.CurrExitLabel:=oldCurrExitLabel;
  724. thlcgwasm(hlcg).exitBr:=oldExitBr;
  725. if assigned(current_procinfo.CurrBreakLabel) then
  726. begin
  727. current_procinfo.CurrContinueLabel:=oldContinueLabel;
  728. thlcgwasm(hlcg).loopContBr:=oldLoopContBr;
  729. current_procinfo.CurrBreakLabel:=oldBreakLabel;
  730. thlcgwasm(hlcg).loopBreakBr:=oldLoopBreakBr;
  731. end;
  732. flowcontrol:=finallyexceptionstate.oldflowcontrol+(finallyexceptionstate.newflowcontrol-[fc_inflowcontrol,fc_catching_exceptions]);
  733. end;
  734. procedure twasmtryfinallynode.pass_generate_code;
  735. begin
  736. if ts_wasm_no_exceptions in current_settings.targetswitches then
  737. pass_generate_code_no_exceptions
  738. else if ts_wasm_js_exceptions in current_settings.targetswitches then
  739. pass_generate_code_js_exceptions
  740. else if ts_wasm_native_exceptions in current_settings.targetswitches then
  741. pass_generate_code_native_exceptions
  742. else
  743. internalerror(2021091704);
  744. end;
  745. initialization
  746. cifnode:=twasmifnode;
  747. cwhilerepeatnode:=twasmwhilerepeatnode;
  748. craisenode:=twasmraisenode;
  749. ctryexceptnode:=twasmtryexceptnode;
  750. ctryfinallynode:=twasmtryfinallynode;
  751. end.