nwasmflw.pas 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002
  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. { twasmonnode }
  65. twasmonnode = class(tcgonnode)
  66. private
  67. procedure pass_generate_code_no_exceptions;
  68. procedure pass_generate_code_js_exceptions;
  69. procedure pass_generate_code_native_exceptions;
  70. public
  71. procedure pass_generate_code;override;
  72. end;
  73. implementation
  74. uses
  75. verbose,globals,systems,globtype,constexp,
  76. symconst,symdef,symsym,symtype,aasmtai,aasmdata,aasmcpu,defutil,defcmp,
  77. procinfo,cgbase,cgexcept,pass_1,pass_2,parabase,compinnr,
  78. cpubase,cpuinfo,
  79. nbas,nld,ncon,ncnv,ncal,ninl,nmem,nadd,nutils,
  80. tgobj,paramgr,
  81. cgutils,hlcgobj,hlcgcpu;
  82. {*****************************************************************************
  83. twasmwhilerepeatnode
  84. *****************************************************************************}
  85. procedure twasmwhilerepeatnode.pass_generate_code_condition;
  86. begin
  87. secondpass(left);
  88. thlcgwasm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,left.resultdef,left.location);
  89. // reversing the condition
  90. if not (lnf_checknegate in loopflags) then
  91. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_i32_eqz));
  92. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br_if,1) );
  93. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  94. end;
  95. procedure twasmwhilerepeatnode.pass_generate_code;
  96. var
  97. lcont,lbreak,lloop,
  98. oldclabel,oldblabel : tasmlabel;
  99. truelabel,falselabel : tasmlabel;
  100. oldflowcontrol : tflowcontrol;
  101. oldloopcontbroffset: Integer;
  102. oldloopbreakbroffset: Integer;
  103. begin
  104. location_reset(location,LOC_VOID,OS_NO);
  105. current_asmdata.getjumplabel(lloop);
  106. current_asmdata.getjumplabel(lcont);
  107. current_asmdata.getjumplabel(lbreak);
  108. oldflowcontrol:=flowcontrol;
  109. oldloopcontbroffset:=thlcgwasm(hlcg).loopContBr;
  110. oldloopbreakbroffset:=thlcgwasm(hlcg).loopBreakBr;
  111. oldclabel:=current_procinfo.CurrContinueLabel;
  112. oldblabel:=current_procinfo.CurrBreakLabel;
  113. include(flowcontrol,fc_inflowcontrol);
  114. exclude(flowcontrol,fc_unwind_loop);
  115. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  116. thlcgwasm(hlcg).incblock;
  117. thlcgwasm(hlcg).loopBreakBr:=thlcgwasm(hlcg).br_blocks;
  118. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_loop));
  119. thlcgwasm(hlcg).incblock;
  120. if lnf_testatbegin in loopflags then
  121. begin
  122. pass_generate_code_condition;
  123. thlcgwasm(hlcg).loopContBr:=thlcgwasm(hlcg).br_blocks;
  124. end else
  125. thlcgwasm(hlcg).loopContBr:=thlcgwasm(hlcg).br_blocks+1;
  126. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  127. thlcgwasm(hlcg).incblock;
  128. current_procinfo.CurrContinueLabel:=lcont;
  129. current_procinfo.CurrBreakLabel:=lbreak;
  130. secondpass(right);
  131. if (lnf_testatbegin in loopflags) then
  132. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,1) ); // jump back to the external loop
  133. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  134. thlcgwasm(hlcg).decblock;
  135. if not (lnf_testatbegin in loopflags) then begin
  136. pass_generate_code_condition;
  137. end;
  138. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,0) ); // jump back to loop
  139. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_loop));
  140. thlcgwasm(hlcg).decblock;
  141. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  142. thlcgwasm(hlcg).decblock;
  143. current_procinfo.CurrContinueLabel:=oldclabel;
  144. current_procinfo.CurrBreakLabel:=oldblabel;
  145. thlcgwasm(hlcg).loopContBr:=oldloopcontbroffset;
  146. thlcgwasm(hlcg).loopBreakBr:=oldloopbreakbroffset;
  147. { a break/continue in a while/repeat block can't be seen outside }
  148. flowcontrol:=oldflowcontrol+(flowcontrol-[fc_break,fc_continue,fc_inflowcontrol]);
  149. end;
  150. {*****************************************************************************
  151. twasmifnode
  152. *****************************************************************************}
  153. procedure twasmifnode.pass_generate_code;
  154. var
  155. oldflowcontrol: tflowcontrol;
  156. begin
  157. // left - condition
  158. // right - then
  159. // t1 - else (optional)
  160. location_reset(location,LOC_VOID,OS_NO);
  161. oldflowcontrol := flowcontrol;
  162. include(flowcontrol,fc_inflowcontrol);
  163. //todo: MOVE all current_asm_data actions to Wasm HL CodeGen
  164. secondpass(left); // condition exprssions
  165. thlcgwasm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,left.resultdef,left.location);
  166. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
  167. thlcgwasm(hlcg).incblock;
  168. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  169. if Assigned(right) then
  170. secondpass(right); // then branchs
  171. if Assigned(t1) then // else branch
  172. begin
  173. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_else));
  174. secondpass(t1);
  175. end;
  176. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
  177. thlcgwasm(hlcg).decblock;
  178. flowcontrol := oldflowcontrol + (flowcontrol - [fc_inflowcontrol]);
  179. end;
  180. {*****************************************************************************
  181. twasmraisenode
  182. *****************************************************************************}
  183. function twasmraisenode.pass_1_no_exceptions : tnode;
  184. var
  185. statements : tstatementnode;
  186. //current_addr : tlabelnode;
  187. raisenode : tcallnode;
  188. begin
  189. result:=internalstatements(statements);
  190. if assigned(left) then
  191. begin
  192. { first para must be a class }
  193. firstpass(left);
  194. { insert needed typeconvs for addr,frame }
  195. if assigned(right) then
  196. begin
  197. { addr }
  198. firstpass(right);
  199. { frame }
  200. if assigned(third) then
  201. firstpass(third)
  202. else
  203. third:=cpointerconstnode.Create(0,voidpointertype);
  204. end
  205. else
  206. begin
  207. third:=cinlinenode.create(in_get_frame,false,nil);
  208. //current_addr:=clabelnode.create(cnothingnode.create,clabelsym.create('$raiseaddr'));
  209. //addstatement(statements,current_addr);
  210. //right:=caddrnode.create(cloadnode.create(current_addr.labsym,current_addr.labsym.owner));
  211. right:=cnilnode.create;
  212. { raise address off by one so we are for sure inside the action area for the raise }
  213. if tf_use_psabieh in target_info.flags then
  214. right:=caddnode.create_internal(addn,right,cordconstnode.create(1,sizesinttype,false));
  215. end;
  216. raisenode:=ccallnode.createintern('fpc_raiseexception',
  217. ccallparanode.create(third,
  218. ccallparanode.create(right,
  219. ccallparanode.create(left,nil)))
  220. );
  221. include(raisenode.callnodeflags,cnf_call_never_returns);
  222. addstatement(statements,raisenode);
  223. end
  224. else
  225. begin
  226. addstatement(statements,ccallnode.createintern('fpc_popaddrstack',nil));
  227. raisenode:=ccallnode.createintern('fpc_reraise',nil);
  228. include(raisenode.callnodeflags,cnf_call_never_returns);
  229. addstatement(statements,raisenode);
  230. end;
  231. left:=nil;
  232. right:=nil;
  233. third:=nil;
  234. end;
  235. function twasmraisenode.pass_1_native_exceptions : tnode;
  236. var
  237. statements : tstatementnode;
  238. //current_addr : tlabelnode;
  239. raisenode : tcallnode;
  240. begin
  241. result:=internalstatements(statements);
  242. if assigned(left) then
  243. begin
  244. { first para must be a class }
  245. firstpass(left);
  246. { insert needed typeconvs for addr,frame }
  247. if assigned(right) then
  248. begin
  249. { addr }
  250. firstpass(right);
  251. { frame }
  252. if assigned(third) then
  253. firstpass(third)
  254. else
  255. third:=cpointerconstnode.Create(0,voidpointertype);
  256. end
  257. else
  258. begin
  259. third:=cinlinenode.create(in_get_frame,false,nil);
  260. //current_addr:=clabelnode.create(cnothingnode.create,clabelsym.create('$raiseaddr'));
  261. //addstatement(statements,current_addr);
  262. //right:=caddrnode.create(cloadnode.create(current_addr.labsym,current_addr.labsym.owner));
  263. right:=cnilnode.create;
  264. { raise address off by one so we are for sure inside the action area for the raise }
  265. if tf_use_psabieh in target_info.flags then
  266. right:=caddnode.create_internal(addn,right,cordconstnode.create(1,sizesinttype,false));
  267. end;
  268. raisenode:=ccallnode.createintern('fpc_raiseexception',
  269. ccallparanode.create(third,
  270. ccallparanode.create(right,
  271. ccallparanode.create(left,nil)))
  272. );
  273. include(raisenode.callnodeflags,cnf_call_never_returns);
  274. addstatement(statements,raisenode);
  275. end
  276. else
  277. begin
  278. //addstatement(statements,ccallnode.createintern('fpc_popaddrstack',nil));
  279. raisenode:=ccallnode.createintern('fpc_reraise',nil);
  280. include(raisenode.callnodeflags,cnf_call_never_returns);
  281. addstatement(statements,raisenode);
  282. end;
  283. left:=nil;
  284. right:=nil;
  285. third:=nil;
  286. end;
  287. function twasmraisenode.pass_1 : tnode;
  288. begin
  289. if ts_wasm_no_exceptions in current_settings.targetswitches then
  290. result:=pass_1_no_exceptions
  291. else if ts_wasm_native_exceptions in current_settings.targetswitches then
  292. result:=pass_1_native_exceptions
  293. else
  294. result:=inherited;
  295. end;
  296. {*****************************************************************************
  297. twasmtryexceptnode
  298. *****************************************************************************}
  299. procedure twasmtryexceptnode.pass_generate_code_no_exceptions;
  300. begin
  301. location_reset(location,LOC_VOID,OS_NO);
  302. secondpass(left);
  303. end;
  304. procedure twasmtryexceptnode.pass_generate_code_js_exceptions;
  305. begin
  306. internalerror(2021091706);
  307. end;
  308. procedure twasmtryexceptnode.pass_generate_code_native_exceptions;
  309. var
  310. trystate,doobjectdestroyandreraisestate: tcgexceptionstatehandler.texceptionstate;
  311. destroytemps,
  312. excepttemps: tcgexceptionstatehandler.texceptiontemps;
  313. afteronflowcontrol: tflowcontrol;
  314. label
  315. errorexit;
  316. begin
  317. location_reset(location,LOC_VOID,OS_NO);
  318. doobjectdestroyandreraisestate:=Default(tcgexceptionstatehandler.texceptionstate);
  319. { Exception temps? We don't need no stinking exception temps! :) }
  320. fillchar(excepttemps,sizeof(excepttemps),0);
  321. reference_reset(excepttemps.envbuf,0,[]);
  322. reference_reset(excepttemps.jmpbuf,0,[]);
  323. reference_reset(excepttemps.reasonbuf,0,[]);
  324. //exceptstate.oldflowcontrol:=flowcontrol;
  325. //flowcontrol:=[fc_inflowcontrol,fc_catching_exceptions];
  326. cexceptionstatehandler.new_exception(current_asmdata.CurrAsmList,excepttemps,tek_except,trystate);
  327. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_try));
  328. thlcgwasm(hlcg).incblock;
  329. { try block }
  330. secondpass(left);
  331. if codegenerror then
  332. goto errorexit;
  333. //exceptionstate.newflowcontrol:=flowcontrol;
  334. //flowcontrol:=exceptionstate.oldflowcontrol;
  335. cexceptionstatehandler.end_try_block(current_asmdata.CurrAsmList,tek_except,excepttemps,trystate,nil);
  336. current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_catch,current_asmdata.WeakRefAsmSymbol(FPC_EXCEPTION_TAG_SYM,AT_WASM_EXCEPTION_TAG)));
  337. flowcontrol:=[fc_inflowcontrol]+trystate.oldflowcontrol*[fc_catching_exceptions];
  338. { on statements }
  339. if assigned(right) then
  340. secondpass(right);
  341. afteronflowcontrol:=flowcontrol;
  342. { default handling except handling }
  343. if assigned(t1) then
  344. begin
  345. { FPC_CATCHES with 'default handler' flag (=-1) need no longer be called,
  346. it doesn't change any state and its return value is ignored (Sergei)
  347. }
  348. { the destruction of the exception object must be also }
  349. { guarded by an exception frame, but it can be omitted }
  350. { if there's no user code in 'except' block }
  351. if not (has_no_code(t1)) then
  352. begin
  353. { if there is an outer frame that catches exceptions, remember this for the "except"
  354. part of this try/except }
  355. flowcontrol:=trystate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions];
  356. { Exception temps? We don't need no stinking exception temps! :) }
  357. fillchar(excepttemps,sizeof(destroytemps),0);
  358. reference_reset(destroytemps.envbuf,0,[]);
  359. reference_reset(destroytemps.jmpbuf,0,[]);
  360. reference_reset(destroytemps.reasonbuf,0,[]);
  361. cexceptionstatehandler.new_exception(current_asmdata.CurrAsmList,destroytemps,tek_except,doobjectdestroyandreraisestate);
  362. { the flowcontrol from the default except-block must be merged
  363. with the flowcontrol flags potentially set by the
  364. on-statements handled above (secondpass(right)), as they are
  365. at the same program level }
  366. flowcontrol:=
  367. flowcontrol+
  368. afteronflowcontrol;
  369. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_try));
  370. thlcgwasm(hlcg).incblock;
  371. secondpass(t1);
  372. hlcg.g_call_system_proc(current_asmdata.CurrAsmList,'fpc_doneexception',[],nil).resetiftemp;
  373. current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_catch,current_asmdata.WeakRefAsmSymbol(FPC_EXCEPTION_TAG_SYM,AT_WASM_EXCEPTION_TAG)));
  374. hlcg.g_call_system_proc(current_asmdata.CurrAsmList,'fpc_raise_nested',[],nil).resetiftemp;
  375. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_try));
  376. thlcgwasm(hlcg).decblock;
  377. end
  378. else
  379. begin
  380. doobjectdestroyandreraisestate.newflowcontrol:=afteronflowcontrol;
  381. hlcg.g_call_system_proc(current_asmdata.CurrAsmList,'fpc_doneexception',[],nil).resetiftemp;
  382. end;
  383. end
  384. else
  385. begin
  386. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_rethrow,0));
  387. doobjectdestroyandreraisestate.newflowcontrol:=afteronflowcontrol;
  388. end;
  389. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_try));
  390. thlcgwasm(hlcg).decblock;
  391. errorexit:
  392. { return all used control flow statements }
  393. flowcontrol:=trystate.oldflowcontrol+(doobjectdestroyandreraisestate.newflowcontrol +
  394. trystate.newflowcontrol - [fc_inflowcontrol,fc_catching_exceptions]);
  395. end;
  396. procedure twasmtryexceptnode.pass_generate_code;
  397. begin
  398. if ts_wasm_no_exceptions in current_settings.targetswitches then
  399. pass_generate_code_no_exceptions
  400. else if ts_wasm_js_exceptions in current_settings.targetswitches then
  401. pass_generate_code_js_exceptions
  402. else if ts_wasm_native_exceptions in current_settings.targetswitches then
  403. pass_generate_code_native_exceptions
  404. else
  405. internalerror(2021091705);
  406. end;
  407. {*****************************************************************************
  408. twasmtryfinallynode
  409. *****************************************************************************}
  410. procedure twasmtryfinallynode.pass_generate_code_no_exceptions;
  411. var
  412. exitfinallylabel,
  413. continuefinallylabel,
  414. breakfinallylabel,
  415. oldCurrExitLabel,
  416. oldContinueLabel,
  417. oldBreakLabel: tasmlabel;
  418. oldLoopContBr: integer;
  419. oldLoopBreakBr: integer;
  420. oldExitBr: integer;
  421. finallyexceptionstate: tcgexceptionstatehandler.texceptionstate;
  422. excepttemps : tcgexceptionstatehandler.texceptiontemps;
  423. exceptframekind: tcgexceptionstatehandler.texceptframekind;
  424. in_loop: Boolean;
  425. procedure generate_exceptreason_check_br(reason: tcgint; br: aint);
  426. var
  427. reasonreg : tregister;
  428. begin
  429. reasonreg:=hlcg.getintregister(current_asmdata.CurrAsmList,exceptionreasontype);
  430. hlcg.g_exception_reason_load(current_asmdata.CurrAsmList,exceptionreasontype,exceptionreasontype,excepttemps.reasonbuf,reasonreg);
  431. thlcgwasm(hlcg).a_cmp_const_reg_stack(current_asmdata.CurrAsmList,exceptionreasontype,OC_EQ,reason,reasonreg);
  432. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
  433. thlcgwasm(hlcg).incblock;
  434. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  435. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,br+1));
  436. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
  437. thlcgwasm(hlcg).decblock;
  438. end;
  439. begin
  440. location_reset(location,LOC_VOID,OS_NO);
  441. oldBreakLabel:=nil;
  442. oldContinueLabel:=nil;
  443. continuefinallylabel:=nil;
  444. breakfinallylabel:=nil;
  445. oldLoopBreakBr:=0;
  446. oldLoopContBr:=0;
  447. in_loop:=assigned(current_procinfo.CurrBreakLabel);
  448. if not implicitframe then
  449. exceptframekind:=tek_normalfinally
  450. else
  451. exceptframekind:=tek_implicitfinally;
  452. { in 'no exceptions' mode, we still want to handle properly exit,
  453. continue and break (they still need to execute the 'finally'
  454. statements), so for this we need excepttemps.reasonbuf, and for this
  455. reason, we need to allocate excepttemps }
  456. cexceptionstatehandler.get_exception_temps(current_asmdata.CurrAsmList,excepttemps);
  457. cexceptionstatehandler.new_exception(current_asmdata.CurrAsmList,excepttemps,exceptframekind,finallyexceptionstate);
  458. { the finally block must catch break, continue and exit }
  459. { statements }
  460. { the outer 'try..finally' block }
  461. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  462. thlcgwasm(hlcg).incblock;
  463. { the 'exit' block }
  464. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  465. thlcgwasm(hlcg).incblock;
  466. oldCurrExitLabel:=current_procinfo.CurrExitLabel;
  467. oldExitBr:=thlcgwasm(hlcg).exitBr;
  468. exitfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  469. current_procinfo.CurrExitLabel:=exitfinallylabel;
  470. thlcgwasm(hlcg).exitBr:=thlcgwasm(hlcg).br_blocks;
  471. { the 'break' block }
  472. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  473. thlcgwasm(hlcg).incblock;
  474. if in_loop then
  475. begin
  476. oldBreakLabel:=current_procinfo.CurrBreakLabel;
  477. oldLoopBreakBr:=thlcgwasm(hlcg).loopBreakBr;
  478. breakfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  479. current_procinfo.CurrBreakLabel:=breakfinallylabel;
  480. thlcgwasm(hlcg).loopBreakBr:=thlcgwasm(hlcg).br_blocks;
  481. end;
  482. { the 'continue' block }
  483. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  484. thlcgwasm(hlcg).incblock;
  485. if in_loop then
  486. begin
  487. oldContinueLabel:=current_procinfo.CurrContinueLabel;
  488. oldLoopContBr:=thlcgwasm(hlcg).loopContBr;
  489. continuefinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  490. current_procinfo.CurrContinueLabel:=continuefinallylabel;
  491. thlcgwasm(hlcg).loopContBr:=thlcgwasm(hlcg).br_blocks;
  492. end;
  493. { try code }
  494. if assigned(left) then
  495. begin
  496. secondpass(left);
  497. if codegenerror then
  498. exit;
  499. end;
  500. { don't generate line info for internal cleanup }
  501. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  502. cexceptionstatehandler.end_try_block(current_asmdata.CurrAsmList,exceptframekind,excepttemps,finallyexceptionstate,nil);
  503. { we've reached the end of the 'try' block, with no exceptions/exit/break/continue, so set exceptionreason:=0 }
  504. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,0,excepttemps.reasonbuf);
  505. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,3)); // jump to the 'finally' section
  506. { exit the 'continue' block }
  507. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  508. thlcgwasm(hlcg).decblock;
  509. { exceptionreason:=4 (continue) }
  510. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,4,excepttemps.reasonbuf);
  511. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,2)); // jump to the 'finally' section
  512. { exit the 'break' block }
  513. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  514. thlcgwasm(hlcg).decblock;
  515. { exceptionreason:=3 (break) }
  516. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,3,excepttemps.reasonbuf);
  517. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,1)); // jump to the 'finally' section
  518. { exit the 'exit' block }
  519. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  520. thlcgwasm(hlcg).decblock;
  521. { exceptionreason:=2 (exit) }
  522. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,2,excepttemps.reasonbuf);
  523. { proceed to the 'finally' section, which follow immediately, no need for jumps }
  524. { exit the outer 'try..finally' block }
  525. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  526. thlcgwasm(hlcg).decblock;
  527. { end cleanup }
  528. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  529. { finally code (don't unconditionally set fc_inflowcontrol, since the
  530. finally code is unconditionally executed; we do have to filter out
  531. flags regarding break/contrinue/etc. because we have to give an
  532. error in case one of those is used in the finally-code }
  533. flowcontrol:=finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions];
  534. secondpass(right);
  535. { goto is allowed if it stays inside the finally block,
  536. this is checked using the exception block number }
  537. if (flowcontrol-[fc_gotolabel])<>(finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions]) then
  538. CGMessage(cg_e_control_flow_outside_finally);
  539. if codegenerror then
  540. exit;
  541. { don't generate line info for internal cleanup }
  542. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  543. if fc_exit in finallyexceptionstate.newflowcontrol then
  544. generate_exceptreason_check_br(2,thlcgwasm(hlcg).br_blocks-oldExitBr);
  545. if fc_break in finallyexceptionstate.newflowcontrol then
  546. generate_exceptreason_check_br(3,thlcgwasm(hlcg).br_blocks-oldLoopBreakBr);
  547. if fc_continue in finallyexceptionstate.newflowcontrol then
  548. generate_exceptreason_check_br(4,thlcgwasm(hlcg).br_blocks-oldLoopContBr);
  549. cexceptionstatehandler.unget_exception_temps(current_asmdata.CurrAsmList,excepttemps);
  550. { end cleanup }
  551. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  552. current_procinfo.CurrExitLabel:=oldCurrExitLabel;
  553. thlcgwasm(hlcg).exitBr:=oldExitBr;
  554. if assigned(current_procinfo.CurrBreakLabel) then
  555. begin
  556. current_procinfo.CurrContinueLabel:=oldContinueLabel;
  557. thlcgwasm(hlcg).loopContBr:=oldLoopContBr;
  558. current_procinfo.CurrBreakLabel:=oldBreakLabel;
  559. thlcgwasm(hlcg).loopBreakBr:=oldLoopBreakBr;
  560. end;
  561. flowcontrol:=finallyexceptionstate.oldflowcontrol+(finallyexceptionstate.newflowcontrol-[fc_inflowcontrol,fc_catching_exceptions]);
  562. end;
  563. procedure twasmtryfinallynode.pass_generate_code_js_exceptions;
  564. begin
  565. internalerror(2021091702);
  566. end;
  567. procedure twasmtryfinallynode.pass_generate_code_native_exceptions;
  568. var
  569. exitfinallylabel,
  570. continuefinallylabel,
  571. breakfinallylabel,
  572. oldCurrExitLabel,
  573. oldContinueLabel,
  574. oldBreakLabel: tasmlabel;
  575. oldLoopContBr: integer;
  576. oldLoopBreakBr: integer;
  577. oldExitBr: integer;
  578. finallyexceptionstate: tcgexceptionstatehandler.texceptionstate;
  579. excepttemps : tcgexceptionstatehandler.texceptiontemps;
  580. exceptframekind: tcgexceptionstatehandler.texceptframekind;
  581. in_loop: Boolean;
  582. procedure generate_exceptreason_check_br(reason: tcgint; br: aint);
  583. var
  584. reasonreg : tregister;
  585. begin
  586. reasonreg:=hlcg.getintregister(current_asmdata.CurrAsmList,exceptionreasontype);
  587. hlcg.g_exception_reason_load(current_asmdata.CurrAsmList,exceptionreasontype,exceptionreasontype,excepttemps.reasonbuf,reasonreg);
  588. thlcgwasm(hlcg).a_cmp_const_reg_stack(current_asmdata.CurrAsmList,exceptionreasontype,OC_EQ,reason,reasonreg);
  589. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
  590. thlcgwasm(hlcg).incblock;
  591. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  592. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,br+1));
  593. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
  594. thlcgwasm(hlcg).decblock;
  595. end;
  596. procedure generate_exceptreason_throw(reason: tcgint);
  597. var
  598. reasonreg : tregister;
  599. begin
  600. reasonreg:=hlcg.getintregister(current_asmdata.CurrAsmList,exceptionreasontype);
  601. hlcg.g_exception_reason_load(current_asmdata.CurrAsmList,exceptionreasontype,exceptionreasontype,excepttemps.reasonbuf,reasonreg);
  602. thlcgwasm(hlcg).a_cmp_const_reg_stack(current_asmdata.CurrAsmList,exceptionreasontype,OC_EQ,reason,reasonreg);
  603. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
  604. thlcgwasm(hlcg).incblock;
  605. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  606. current_asmdata.CurrAsmList.Concat(taicpu.op_sym(a_throw,current_asmdata.WeakRefAsmSymbol(FPC_EXCEPTION_TAG_SYM,AT_WASM_EXCEPTION_TAG)));
  607. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
  608. thlcgwasm(hlcg).decblock;
  609. end;
  610. begin
  611. location_reset(location,LOC_VOID,OS_NO);
  612. oldBreakLabel:=nil;
  613. oldContinueLabel:=nil;
  614. continuefinallylabel:=nil;
  615. breakfinallylabel:=nil;
  616. oldLoopBreakBr:=0;
  617. oldLoopContBr:=0;
  618. in_loop:=assigned(current_procinfo.CurrBreakLabel);
  619. if not implicitframe then
  620. exceptframekind:=tek_normalfinally
  621. else
  622. exceptframekind:=tek_implicitfinally;
  623. { in 'no exceptions' mode, we still want to handle properly exit,
  624. continue and break (they still need to execute the 'finally'
  625. statements), so for this we need excepttemps.reasonbuf, and for this
  626. reason, we need to allocate excepttemps }
  627. cexceptionstatehandler.get_exception_temps(current_asmdata.CurrAsmList,excepttemps);
  628. cexceptionstatehandler.new_exception(current_asmdata.CurrAsmList,excepttemps,exceptframekind,finallyexceptionstate);
  629. { the finally block must catch break, continue and exit }
  630. { statements }
  631. { the outer 'try..finally' block }
  632. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  633. thlcgwasm(hlcg).incblock;
  634. { the 'exit' block }
  635. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  636. thlcgwasm(hlcg).incblock;
  637. oldCurrExitLabel:=current_procinfo.CurrExitLabel;
  638. oldExitBr:=thlcgwasm(hlcg).exitBr;
  639. exitfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  640. current_procinfo.CurrExitLabel:=exitfinallylabel;
  641. thlcgwasm(hlcg).exitBr:=thlcgwasm(hlcg).br_blocks;
  642. { the 'break' block }
  643. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  644. thlcgwasm(hlcg).incblock;
  645. if in_loop then
  646. begin
  647. oldBreakLabel:=current_procinfo.CurrBreakLabel;
  648. oldLoopBreakBr:=thlcgwasm(hlcg).loopBreakBr;
  649. breakfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  650. current_procinfo.CurrBreakLabel:=breakfinallylabel;
  651. thlcgwasm(hlcg).loopBreakBr:=thlcgwasm(hlcg).br_blocks;
  652. end;
  653. { the 'continue' block }
  654. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block));
  655. thlcgwasm(hlcg).incblock;
  656. if in_loop then
  657. begin
  658. oldContinueLabel:=current_procinfo.CurrContinueLabel;
  659. oldLoopContBr:=thlcgwasm(hlcg).loopContBr;
  660. continuefinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate);
  661. current_procinfo.CurrContinueLabel:=continuefinallylabel;
  662. thlcgwasm(hlcg).loopContBr:=thlcgwasm(hlcg).br_blocks;
  663. end;
  664. { the inner 'try..end_try' block }
  665. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_try));
  666. thlcgwasm(hlcg).incblock;
  667. { try code }
  668. if assigned(left) then
  669. begin
  670. secondpass(left);
  671. if codegenerror then
  672. exit;
  673. end;
  674. { don't generate line info for internal cleanup }
  675. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  676. cexceptionstatehandler.end_try_block(current_asmdata.CurrAsmList,exceptframekind,excepttemps,finallyexceptionstate,nil);
  677. { we've reached the end of the 'try' block, with no exceptions/exit/break/continue, so set exceptionreason:=0 }
  678. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,0,excepttemps.reasonbuf);
  679. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,4)); // jump to the 'finally' section
  680. current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_catch,current_asmdata.WeakRefAsmSymbol(FPC_EXCEPTION_TAG_SYM,AT_WASM_EXCEPTION_TAG)));
  681. { exceptionreason:=1 (exception) }
  682. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,1,excepttemps.reasonbuf);
  683. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,4)); // jump to the 'finally' section
  684. { exit the inner 'try..end_try' block }
  685. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_try));
  686. thlcgwasm(hlcg).decblock;
  687. { exit the 'continue' block }
  688. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  689. thlcgwasm(hlcg).decblock;
  690. { exceptionreason:=4 (continue) }
  691. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,4,excepttemps.reasonbuf);
  692. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,2)); // jump to the 'finally' section
  693. { exit the 'break' block }
  694. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  695. thlcgwasm(hlcg).decblock;
  696. { exceptionreason:=3 (break) }
  697. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,3,excepttemps.reasonbuf);
  698. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,1)); // jump to the 'finally' section
  699. { exit the 'exit' block }
  700. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  701. thlcgwasm(hlcg).decblock;
  702. { exceptionreason:=2 (exit) }
  703. hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,2,excepttemps.reasonbuf);
  704. { proceed to the 'finally' section, which follow immediately, no need for jumps }
  705. { exit the outer 'try..finally' block }
  706. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block));
  707. thlcgwasm(hlcg).decblock;
  708. { end cleanup }
  709. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  710. { finally code (don't unconditionally set fc_inflowcontrol, since the
  711. finally code is unconditionally executed; we do have to filter out
  712. flags regarding break/contrinue/etc. because we have to give an
  713. error in case one of those is used in the finally-code }
  714. flowcontrol:=finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions];
  715. secondpass(right);
  716. { goto is allowed if it stays inside the finally block,
  717. this is checked using the exception block number }
  718. if (flowcontrol-[fc_gotolabel])<>(finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions]) then
  719. CGMessage(cg_e_control_flow_outside_finally);
  720. if codegenerror then
  721. exit;
  722. { don't generate line info for internal cleanup }
  723. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  724. if fc_exit in finallyexceptionstate.newflowcontrol then
  725. generate_exceptreason_check_br(2,thlcgwasm(hlcg).br_blocks-oldExitBr);
  726. if fc_break in finallyexceptionstate.newflowcontrol then
  727. generate_exceptreason_check_br(3,thlcgwasm(hlcg).br_blocks-oldLoopBreakBr);
  728. if fc_continue in finallyexceptionstate.newflowcontrol then
  729. generate_exceptreason_check_br(4,thlcgwasm(hlcg).br_blocks-oldLoopContBr);
  730. generate_exceptreason_throw(1);
  731. cexceptionstatehandler.unget_exception_temps(current_asmdata.CurrAsmList,excepttemps);
  732. { end cleanup }
  733. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  734. current_procinfo.CurrExitLabel:=oldCurrExitLabel;
  735. thlcgwasm(hlcg).exitBr:=oldExitBr;
  736. if assigned(current_procinfo.CurrBreakLabel) then
  737. begin
  738. current_procinfo.CurrContinueLabel:=oldContinueLabel;
  739. thlcgwasm(hlcg).loopContBr:=oldLoopContBr;
  740. current_procinfo.CurrBreakLabel:=oldBreakLabel;
  741. thlcgwasm(hlcg).loopBreakBr:=oldLoopBreakBr;
  742. end;
  743. flowcontrol:=finallyexceptionstate.oldflowcontrol+(finallyexceptionstate.newflowcontrol-[fc_inflowcontrol,fc_catching_exceptions]);
  744. end;
  745. procedure twasmtryfinallynode.pass_generate_code;
  746. begin
  747. if ts_wasm_no_exceptions in current_settings.targetswitches then
  748. pass_generate_code_no_exceptions
  749. else if ts_wasm_js_exceptions in current_settings.targetswitches then
  750. pass_generate_code_js_exceptions
  751. else if ts_wasm_native_exceptions in current_settings.targetswitches then
  752. pass_generate_code_native_exceptions
  753. else
  754. internalerror(2021091704);
  755. end;
  756. {*****************************************************************************
  757. twasmonnode
  758. *****************************************************************************}
  759. procedure twasmonnode.pass_generate_code_no_exceptions;
  760. begin
  761. { should not be called }
  762. internalerror(2021092803);
  763. end;
  764. procedure twasmonnode.pass_generate_code_js_exceptions;
  765. begin
  766. { not yet implemented }
  767. internalerror(2021092804);
  768. end;
  769. procedure twasmonnode.pass_generate_code_native_exceptions;
  770. var
  771. exceptvarsym : tlocalvarsym;
  772. exceptlocdef: tdef;
  773. exceptlocreg: tregister;
  774. begin
  775. location_reset(location,LOC_VOID,OS_NO);
  776. cexceptionstatehandler.begin_catch(current_asmdata.CurrAsmList,excepttype,nil,exceptlocdef,exceptlocreg);
  777. { Retrieve exception variable }
  778. if assigned(excepTSymtable) then
  779. exceptvarsym:=tlocalvarsym(excepTSymtable.SymList[0])
  780. else
  781. internalerror(2011020401);
  782. if assigned(exceptvarsym) then
  783. begin
  784. location_reset_ref(exceptvarsym.localloc, LOC_REFERENCE, def_cgsize(voidpointertype), voidpointertype.alignment, []);
  785. tg.GetLocal(current_asmdata.CurrAsmList, exceptvarsym.vardef.size, exceptvarsym.vardef, exceptvarsym.localloc.reference);
  786. hlcg.a_load_reg_ref(current_asmdata.CurrAsmList, exceptlocdef, exceptvarsym.vardef, exceptlocreg, exceptvarsym.localloc.reference);
  787. end;
  788. { in the case that another exception is risen
  789. we've to destroy the old one, so create a new
  790. exception frame for the catch-handler }
  791. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_try));
  792. thlcgwasm(hlcg).incblock;
  793. if assigned(right) then
  794. secondpass(right);
  795. hlcg.g_call_system_proc(current_asmdata.CurrAsmList,'fpc_doneexception',[],nil).resetiftemp;
  796. current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,2));
  797. current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_catch,current_asmdata.WeakRefAsmSymbol(FPC_EXCEPTION_TAG_SYM,AT_WASM_EXCEPTION_TAG)));
  798. hlcg.g_call_system_proc(current_asmdata.CurrAsmList,'fpc_raise_nested',[],nil).resetiftemp;
  799. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_try));
  800. thlcgwasm(hlcg).decblock;
  801. { clear some stuff }
  802. if assigned(exceptvarsym) then
  803. begin
  804. tg.UngetLocal(current_asmdata.CurrAsmList,exceptvarsym.localloc.reference);
  805. exceptvarsym.localloc.loc:=LOC_INVALID;
  806. end;
  807. cexceptionstatehandler.end_catch(current_asmdata.CurrAsmList);
  808. { next on node }
  809. if assigned(left) then
  810. secondpass(left);
  811. end;
  812. procedure twasmonnode.pass_generate_code;
  813. begin
  814. if ts_wasm_no_exceptions in current_settings.targetswitches then
  815. pass_generate_code_no_exceptions
  816. else if ts_wasm_js_exceptions in current_settings.targetswitches then
  817. pass_generate_code_js_exceptions
  818. else if ts_wasm_native_exceptions in current_settings.targetswitches then
  819. pass_generate_code_native_exceptions
  820. else
  821. internalerror(2021092802);
  822. end;
  823. initialization
  824. cifnode:=twasmifnode;
  825. cwhilerepeatnode:=twasmwhilerepeatnode;
  826. craisenode:=twasmraisenode;
  827. ctryexceptnode:=twasmtryexceptnode;
  828. ctryfinallynode:=twasmtryfinallynode;
  829. connode:=twasmonnode;
  830. end.