{ Copyright (c) 2019 by Dmitry Boyarintsev Generate assembler for nodes that influence the flow for the JVM This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. **************************************************************************** } unit nwasmflw; {$i fpcdefs.inc} interface uses aasmbase,node,nflw,ncgflw, cutils; type { twasmifnode } { Wasm doesn't have any jump(+offset) operations It only provide structured blockes to handle jumps (It's possible to jump-out-of-block at any time) "If" is also implemented as a block, identical to high-level language. } twasmifnode = class(tcgifnode) public procedure pass_generate_code;override; end; { twasmwhilerepeatnode } twasmwhilerepeatnode = class(tcgwhilerepeatnode) public procedure pass_generate_code_condition; procedure pass_generate_code;override; end; { twasmraisenode } twasmraisenode = class(tcgraisenode) private function pass_1_no_exceptions : tnode; function pass_1_native_exceptions : tnode; public function pass_1 : tnode;override; end; { twasmtryexceptnode } twasmtryexceptnode = class(tcgtryexceptnode) private procedure pass_generate_code_no_exceptions; procedure pass_generate_code_js_exceptions; procedure pass_generate_code_native_exceptions; public procedure pass_generate_code;override; end; { twasmtryfinallynode } twasmtryfinallynode = class(tcgtryfinallynode) private procedure pass_generate_code_no_exceptions; procedure pass_generate_code_js_exceptions; procedure pass_generate_code_native_exceptions; public procedure pass_generate_code;override; end; implementation uses verbose,globals,systems,globtype,constexp, symconst,symdef,symsym,aasmtai,aasmdata,aasmcpu,defutil,defcmp, procinfo,cgbase,cgexcept,pass_1,pass_2,parabase,compinnr, cpubase,cpuinfo, nbas,nld,ncon,ncnv,ncal,ninl,nmem,nadd, tgobj,paramgr, cgutils,hlcgobj,hlcgcpu; {***************************************************************************** twasmwhilerepeatnode *****************************************************************************} procedure twasmwhilerepeatnode.pass_generate_code_condition; begin secondpass(left); thlcgwasm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,left.resultdef,left.location); // reversing the condition if not (lnf_checknegate in loopflags) then current_asmdata.CurrAsmList.concat(taicpu.op_none(a_i32_eqz)); current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br_if,1) ); thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1); end; procedure twasmwhilerepeatnode.pass_generate_code; var lcont,lbreak,lloop, oldclabel,oldblabel : tasmlabel; truelabel,falselabel : tasmlabel; oldflowcontrol : tflowcontrol; oldloopcontbroffset: Integer; oldloopbreakbroffset: Integer; begin location_reset(location,LOC_VOID,OS_NO); current_asmdata.getjumplabel(lloop); current_asmdata.getjumplabel(lcont); current_asmdata.getjumplabel(lbreak); oldflowcontrol:=flowcontrol; oldloopcontbroffset:=thlcgwasm(hlcg).loopContBr; oldloopbreakbroffset:=thlcgwasm(hlcg).loopBreakBr; oldclabel:=current_procinfo.CurrContinueLabel; oldblabel:=current_procinfo.CurrBreakLabel; include(flowcontrol,fc_inflowcontrol); exclude(flowcontrol,fc_unwind_loop); current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block)); thlcgwasm(hlcg).incblock; thlcgwasm(hlcg).loopBreakBr:=thlcgwasm(hlcg).br_blocks; current_asmdata.CurrAsmList.concat(taicpu.op_none(a_loop)); thlcgwasm(hlcg).incblock; if lnf_testatbegin in loopflags then begin pass_generate_code_condition; thlcgwasm(hlcg).loopContBr:=thlcgwasm(hlcg).br_blocks; end else thlcgwasm(hlcg).loopContBr:=thlcgwasm(hlcg).br_blocks+1; current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block)); thlcgwasm(hlcg).incblock; current_procinfo.CurrContinueLabel:=lcont; current_procinfo.CurrBreakLabel:=lbreak; secondpass(right); if (lnf_testatbegin in loopflags) then current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,1) ); // jump back to the external loop current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block)); thlcgwasm(hlcg).decblock; if not (lnf_testatbegin in loopflags) then begin pass_generate_code_condition; end; current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,0) ); // jump back to loop current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_loop)); thlcgwasm(hlcg).decblock; current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block)); thlcgwasm(hlcg).decblock; current_procinfo.CurrContinueLabel:=oldclabel; current_procinfo.CurrBreakLabel:=oldblabel; thlcgwasm(hlcg).loopContBr:=oldloopcontbroffset; thlcgwasm(hlcg).loopBreakBr:=oldloopbreakbroffset; { a break/continue in a while/repeat block can't be seen outside } flowcontrol:=oldflowcontrol+(flowcontrol-[fc_break,fc_continue,fc_inflowcontrol]); end; {***************************************************************************** twasmifnode *****************************************************************************} procedure twasmifnode.pass_generate_code; var oldflowcontrol: tflowcontrol; begin // left - condition // right - then // t1 - else (optional) location_reset(location,LOC_VOID,OS_NO); oldflowcontrol := flowcontrol; include(flowcontrol,fc_inflowcontrol); //todo: MOVE all current_asm_data actions to Wasm HL CodeGen secondpass(left); // condition exprssions thlcgwasm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,left.resultdef,left.location); current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if)); thlcgwasm(hlcg).incblock; thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1); if Assigned(right) then secondpass(right); // then branchs if Assigned(t1) then // else branch begin current_asmdata.CurrAsmList.concat(taicpu.op_none(a_else)); secondpass(t1); end; current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if)); thlcgwasm(hlcg).decblock; flowcontrol := oldflowcontrol + (flowcontrol - [fc_inflowcontrol]); end; {***************************************************************************** twasmraisenode *****************************************************************************} function twasmraisenode.pass_1_no_exceptions : tnode; var statements : tstatementnode; //current_addr : tlabelnode; raisenode : tcallnode; begin result:=internalstatements(statements); if assigned(left) then begin { first para must be a class } firstpass(left); { insert needed typeconvs for addr,frame } if assigned(right) then begin { addr } firstpass(right); { frame } if assigned(third) then firstpass(third) else third:=cpointerconstnode.Create(0,voidpointertype); end else begin third:=cinlinenode.create(in_get_frame,false,nil); //current_addr:=clabelnode.create(cnothingnode.create,clabelsym.create('$raiseaddr')); //addstatement(statements,current_addr); //right:=caddrnode.create(cloadnode.create(current_addr.labsym,current_addr.labsym.owner)); right:=cnilnode.create; { raise address off by one so we are for sure inside the action area for the raise } if tf_use_psabieh in target_info.flags then right:=caddnode.create_internal(addn,right,cordconstnode.create(1,sizesinttype,false)); end; raisenode:=ccallnode.createintern('fpc_raiseexception', ccallparanode.create(third, ccallparanode.create(right, ccallparanode.create(left,nil))) ); include(raisenode.callnodeflags,cnf_call_never_returns); addstatement(statements,raisenode); end else begin addstatement(statements,ccallnode.createintern('fpc_popaddrstack',nil)); raisenode:=ccallnode.createintern('fpc_reraise',nil); include(raisenode.callnodeflags,cnf_call_never_returns); addstatement(statements,raisenode); end; left:=nil; right:=nil; third:=nil; end; function twasmraisenode.pass_1_native_exceptions : tnode; var statements : tstatementnode; //current_addr : tlabelnode; raisenode : tcallnode; begin result:=internalstatements(statements); if assigned(left) then begin { first para must be a class } firstpass(left); { insert needed typeconvs for addr,frame } if assigned(right) then begin { addr } firstpass(right); { frame } if assigned(third) then firstpass(third) else third:=cpointerconstnode.Create(0,voidpointertype); end else begin third:=cinlinenode.create(in_get_frame,false,nil); //current_addr:=clabelnode.create(cnothingnode.create,clabelsym.create('$raiseaddr')); //addstatement(statements,current_addr); //right:=caddrnode.create(cloadnode.create(current_addr.labsym,current_addr.labsym.owner)); right:=cnilnode.create; { raise address off by one so we are for sure inside the action area for the raise } if tf_use_psabieh in target_info.flags then right:=caddnode.create_internal(addn,right,cordconstnode.create(1,sizesinttype,false)); end; raisenode:=ccallnode.createintern('fpc_raiseexception', ccallparanode.create(third, ccallparanode.create(right, ccallparanode.create(left,nil))) ); include(raisenode.callnodeflags,cnf_call_never_returns); addstatement(statements,raisenode); end else begin //addstatement(statements,ccallnode.createintern('fpc_popaddrstack',nil)); raisenode:=ccallnode.createintern('fpc_reraise',nil); include(raisenode.callnodeflags,cnf_call_never_returns); addstatement(statements,raisenode); end; left:=nil; right:=nil; third:=nil; end; function twasmraisenode.pass_1 : tnode; begin if ts_wasm_no_exceptions in current_settings.targetswitches then result:=pass_1_no_exceptions else if ts_wasm_native_exceptions in current_settings.targetswitches then result:=pass_1_native_exceptions else result:=inherited; end; {***************************************************************************** twasmtryexceptnode *****************************************************************************} procedure twasmtryexceptnode.pass_generate_code_no_exceptions; begin location_reset(location,LOC_VOID,OS_NO); secondpass(left); end; procedure twasmtryexceptnode.pass_generate_code_js_exceptions; begin internalerror(2021091706); end; procedure twasmtryexceptnode.pass_generate_code_native_exceptions; begin location_reset(location,LOC_VOID,OS_NO); secondpass(left); end; procedure twasmtryexceptnode.pass_generate_code; begin if ts_wasm_no_exceptions in current_settings.targetswitches then pass_generate_code_no_exceptions else if ts_wasm_js_exceptions in current_settings.targetswitches then pass_generate_code_js_exceptions else if ts_wasm_native_exceptions in current_settings.targetswitches then pass_generate_code_native_exceptions else internalerror(2021091705); end; {***************************************************************************** twasmtryfinallynode *****************************************************************************} procedure twasmtryfinallynode.pass_generate_code_no_exceptions; var exitfinallylabel, continuefinallylabel, breakfinallylabel, oldCurrExitLabel, oldContinueLabel, oldBreakLabel: tasmlabel; oldLoopContBr: integer; oldLoopBreakBr: integer; oldExitBr: integer; finallyexceptionstate: tcgexceptionstatehandler.texceptionstate; excepttemps : tcgexceptionstatehandler.texceptiontemps; exceptframekind: tcgexceptionstatehandler.texceptframekind; in_loop: Boolean; procedure generate_exceptreason_check_br(reason: tcgint; br: aint); var reasonreg : tregister; begin reasonreg:=hlcg.getintregister(current_asmdata.CurrAsmList,exceptionreasontype); hlcg.g_exception_reason_load(current_asmdata.CurrAsmList,exceptionreasontype,exceptionreasontype,excepttemps.reasonbuf,reasonreg); thlcgwasm(hlcg).a_cmp_const_reg_stack(current_asmdata.CurrAsmList,exceptionreasontype,OC_EQ,reason,reasonreg); current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if)); thlcgwasm(hlcg).incblock; thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1); current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,br+1)); current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if)); thlcgwasm(hlcg).decblock; end; begin location_reset(location,LOC_VOID,OS_NO); oldBreakLabel:=nil; oldContinueLabel:=nil; continuefinallylabel:=nil; breakfinallylabel:=nil; oldLoopBreakBr:=0; oldLoopContBr:=0; in_loop:=assigned(current_procinfo.CurrBreakLabel); if not implicitframe then exceptframekind:=tek_normalfinally else exceptframekind:=tek_implicitfinally; { in 'no exceptions' mode, we still want to handle properly exit, continue and break (they still need to execute the 'finally' statements), so for this we need excepttemps.reasonbuf, and for this reason, we need to allocate excepttemps } cexceptionstatehandler.get_exception_temps(current_asmdata.CurrAsmList,excepttemps); cexceptionstatehandler.new_exception(current_asmdata.CurrAsmList,excepttemps,exceptframekind,finallyexceptionstate); { the finally block must catch break, continue and exit } { statements } { the outer 'try..finally' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block)); thlcgwasm(hlcg).incblock; { the 'exit' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block)); thlcgwasm(hlcg).incblock; oldCurrExitLabel:=current_procinfo.CurrExitLabel; oldExitBr:=thlcgwasm(hlcg).exitBr; exitfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate); current_procinfo.CurrExitLabel:=exitfinallylabel; thlcgwasm(hlcg).exitBr:=thlcgwasm(hlcg).br_blocks; { the 'break' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block)); thlcgwasm(hlcg).incblock; if in_loop then begin oldBreakLabel:=current_procinfo.CurrBreakLabel; oldLoopBreakBr:=thlcgwasm(hlcg).loopBreakBr; breakfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate); current_procinfo.CurrBreakLabel:=breakfinallylabel; thlcgwasm(hlcg).loopBreakBr:=thlcgwasm(hlcg).br_blocks; end; { the 'continue' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block)); thlcgwasm(hlcg).incblock; if in_loop then begin oldContinueLabel:=current_procinfo.CurrContinueLabel; oldLoopContBr:=thlcgwasm(hlcg).loopContBr; continuefinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate); current_procinfo.CurrContinueLabel:=continuefinallylabel; thlcgwasm(hlcg).loopContBr:=thlcgwasm(hlcg).br_blocks; end; { try code } if assigned(left) then begin secondpass(left); if codegenerror then exit; end; { don't generate line info for internal cleanup } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart)); cexceptionstatehandler.end_try_block(current_asmdata.CurrAsmList,exceptframekind,excepttemps,finallyexceptionstate,nil); { we've reached the end of the 'try' block, with no exceptions/exit/break/continue, so set exceptionreason:=0 } hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,0,excepttemps.reasonbuf); current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,3)); // jump to the 'finally' section { exit the 'continue' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block)); thlcgwasm(hlcg).decblock; { exceptionreason:=4 (continue) } hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,4,excepttemps.reasonbuf); current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,2)); // jump to the 'finally' section { exit the 'break' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block)); thlcgwasm(hlcg).decblock; { exceptionreason:=3 (break) } hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,3,excepttemps.reasonbuf); current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,1)); // jump to the 'finally' section { exit the 'exit' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block)); thlcgwasm(hlcg).decblock; { exceptionreason:=2 (exit) } hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,2,excepttemps.reasonbuf); { proceed to the 'finally' section, which follow immediately, no need for jumps } { exit the outer 'try..finally' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block)); thlcgwasm(hlcg).decblock; { end cleanup } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd)); { finally code (don't unconditionally set fc_inflowcontrol, since the finally code is unconditionally executed; we do have to filter out flags regarding break/contrinue/etc. because we have to give an error in case one of those is used in the finally-code } flowcontrol:=finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions]; secondpass(right); { goto is allowed if it stays inside the finally block, this is checked using the exception block number } if (flowcontrol-[fc_gotolabel])<>(finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions]) then CGMessage(cg_e_control_flow_outside_finally); if codegenerror then exit; { don't generate line info for internal cleanup } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart)); if fc_exit in finallyexceptionstate.newflowcontrol then generate_exceptreason_check_br(2,thlcgwasm(hlcg).br_blocks-oldExitBr); if fc_break in finallyexceptionstate.newflowcontrol then generate_exceptreason_check_br(3,thlcgwasm(hlcg).br_blocks-oldLoopBreakBr); if fc_continue in finallyexceptionstate.newflowcontrol then generate_exceptreason_check_br(4,thlcgwasm(hlcg).br_blocks-oldLoopContBr); cexceptionstatehandler.unget_exception_temps(current_asmdata.CurrAsmList,excepttemps); { end cleanup } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd)); current_procinfo.CurrExitLabel:=oldCurrExitLabel; thlcgwasm(hlcg).exitBr:=oldExitBr; if assigned(current_procinfo.CurrBreakLabel) then begin current_procinfo.CurrContinueLabel:=oldContinueLabel; thlcgwasm(hlcg).loopContBr:=oldLoopContBr; current_procinfo.CurrBreakLabel:=oldBreakLabel; thlcgwasm(hlcg).loopBreakBr:=oldLoopBreakBr; end; flowcontrol:=finallyexceptionstate.oldflowcontrol+(finallyexceptionstate.newflowcontrol-[fc_inflowcontrol,fc_catching_exceptions]); end; procedure twasmtryfinallynode.pass_generate_code_js_exceptions; begin internalerror(2021091702); end; procedure twasmtryfinallynode.pass_generate_code_native_exceptions; var exitfinallylabel, continuefinallylabel, breakfinallylabel, oldCurrExitLabel, oldContinueLabel, oldBreakLabel: tasmlabel; oldLoopContBr: integer; oldLoopBreakBr: integer; oldExitBr: integer; finallyexceptionstate: tcgexceptionstatehandler.texceptionstate; excepttemps : tcgexceptionstatehandler.texceptiontemps; exceptframekind: tcgexceptionstatehandler.texceptframekind; in_loop: Boolean; procedure generate_exceptreason_check_br(reason: tcgint; br: aint); var reasonreg : tregister; begin reasonreg:=hlcg.getintregister(current_asmdata.CurrAsmList,exceptionreasontype); hlcg.g_exception_reason_load(current_asmdata.CurrAsmList,exceptionreasontype,exceptionreasontype,excepttemps.reasonbuf,reasonreg); thlcgwasm(hlcg).a_cmp_const_reg_stack(current_asmdata.CurrAsmList,exceptionreasontype,OC_EQ,reason,reasonreg); current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if)); thlcgwasm(hlcg).incblock; thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1); current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,br+1)); current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if)); thlcgwasm(hlcg).decblock; end; procedure generate_exceptreason_throw(reason: tcgint); var reasonreg : tregister; begin reasonreg:=hlcg.getintregister(current_asmdata.CurrAsmList,exceptionreasontype); hlcg.g_exception_reason_load(current_asmdata.CurrAsmList,exceptionreasontype,exceptionreasontype,excepttemps.reasonbuf,reasonreg); thlcgwasm(hlcg).a_cmp_const_reg_stack(current_asmdata.CurrAsmList,exceptionreasontype,OC_EQ,reason,reasonreg); current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if)); thlcgwasm(hlcg).incblock; thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1); current_asmdata.CurrAsmList.Concat(taicpu.op_sym(a_throw,current_asmdata.WeakRefAsmSymbol(FPC_EXCEPTION_TAG_SYM,AT_WASM_EXCEPTION_TAG))); current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if)); thlcgwasm(hlcg).decblock; end; begin location_reset(location,LOC_VOID,OS_NO); oldBreakLabel:=nil; oldContinueLabel:=nil; continuefinallylabel:=nil; breakfinallylabel:=nil; oldLoopBreakBr:=0; oldLoopContBr:=0; in_loop:=assigned(current_procinfo.CurrBreakLabel); if not implicitframe then exceptframekind:=tek_normalfinally else exceptframekind:=tek_implicitfinally; { in 'no exceptions' mode, we still want to handle properly exit, continue and break (they still need to execute the 'finally' statements), so for this we need excepttemps.reasonbuf, and for this reason, we need to allocate excepttemps } cexceptionstatehandler.get_exception_temps(current_asmdata.CurrAsmList,excepttemps); cexceptionstatehandler.new_exception(current_asmdata.CurrAsmList,excepttemps,exceptframekind,finallyexceptionstate); { the finally block must catch break, continue and exit } { statements } { the outer 'try..finally' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block)); thlcgwasm(hlcg).incblock; { the 'exit' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block)); thlcgwasm(hlcg).incblock; oldCurrExitLabel:=current_procinfo.CurrExitLabel; oldExitBr:=thlcgwasm(hlcg).exitBr; exitfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate); current_procinfo.CurrExitLabel:=exitfinallylabel; thlcgwasm(hlcg).exitBr:=thlcgwasm(hlcg).br_blocks; { the 'break' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block)); thlcgwasm(hlcg).incblock; if in_loop then begin oldBreakLabel:=current_procinfo.CurrBreakLabel; oldLoopBreakBr:=thlcgwasm(hlcg).loopBreakBr; breakfinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate); current_procinfo.CurrBreakLabel:=breakfinallylabel; thlcgwasm(hlcg).loopBreakBr:=thlcgwasm(hlcg).br_blocks; end; { the 'continue' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_block)); thlcgwasm(hlcg).incblock; if in_loop then begin oldContinueLabel:=current_procinfo.CurrContinueLabel; oldLoopContBr:=thlcgwasm(hlcg).loopContBr; continuefinallylabel:=get_jump_out_of_try_finally_frame_label(finallyexceptionstate); current_procinfo.CurrContinueLabel:=continuefinallylabel; thlcgwasm(hlcg).loopContBr:=thlcgwasm(hlcg).br_blocks; end; { the inner 'try..end_try' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_try)); thlcgwasm(hlcg).incblock; { try code } if assigned(left) then begin secondpass(left); if codegenerror then exit; end; { don't generate line info for internal cleanup } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart)); cexceptionstatehandler.end_try_block(current_asmdata.CurrAsmList,exceptframekind,excepttemps,finallyexceptionstate,nil); { we've reached the end of the 'try' block, with no exceptions/exit/break/continue, so set exceptionreason:=0 } hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,0,excepttemps.reasonbuf); current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,4)); // jump to the 'finally' section current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_catch,current_asmdata.WeakRefAsmSymbol(FPC_EXCEPTION_TAG_SYM,AT_WASM_EXCEPTION_TAG))); { exceptionreason:=1 (exception) } hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,1,excepttemps.reasonbuf); current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,4)); // jump to the 'finally' section { exit the inner 'try..end_try' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_try)); thlcgwasm(hlcg).decblock; { exit the 'continue' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block)); thlcgwasm(hlcg).decblock; { exceptionreason:=4 (continue) } hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,4,excepttemps.reasonbuf); current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,2)); // jump to the 'finally' section { exit the 'break' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block)); thlcgwasm(hlcg).decblock; { exceptionreason:=3 (break) } hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,3,excepttemps.reasonbuf); current_asmdata.CurrAsmList.concat(taicpu.op_const(a_br,1)); // jump to the 'finally' section { exit the 'exit' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block)); thlcgwasm(hlcg).decblock; { exceptionreason:=2 (exit) } hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,exceptionreasontype,2,excepttemps.reasonbuf); { proceed to the 'finally' section, which follow immediately, no need for jumps } { exit the outer 'try..finally' block } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_block)); thlcgwasm(hlcg).decblock; { end cleanup } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd)); { finally code (don't unconditionally set fc_inflowcontrol, since the finally code is unconditionally executed; we do have to filter out flags regarding break/contrinue/etc. because we have to give an error in case one of those is used in the finally-code } flowcontrol:=finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions]; secondpass(right); { goto is allowed if it stays inside the finally block, this is checked using the exception block number } if (flowcontrol-[fc_gotolabel])<>(finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions]) then CGMessage(cg_e_control_flow_outside_finally); if codegenerror then exit; { don't generate line info for internal cleanup } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart)); if fc_exit in finallyexceptionstate.newflowcontrol then generate_exceptreason_check_br(2,thlcgwasm(hlcg).br_blocks-oldExitBr); if fc_break in finallyexceptionstate.newflowcontrol then generate_exceptreason_check_br(3,thlcgwasm(hlcg).br_blocks-oldLoopBreakBr); if fc_continue in finallyexceptionstate.newflowcontrol then generate_exceptreason_check_br(4,thlcgwasm(hlcg).br_blocks-oldLoopContBr); generate_exceptreason_throw(1); cexceptionstatehandler.unget_exception_temps(current_asmdata.CurrAsmList,excepttemps); { end cleanup } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd)); current_procinfo.CurrExitLabel:=oldCurrExitLabel; thlcgwasm(hlcg).exitBr:=oldExitBr; if assigned(current_procinfo.CurrBreakLabel) then begin current_procinfo.CurrContinueLabel:=oldContinueLabel; thlcgwasm(hlcg).loopContBr:=oldLoopContBr; current_procinfo.CurrBreakLabel:=oldBreakLabel; thlcgwasm(hlcg).loopBreakBr:=oldLoopBreakBr; end; flowcontrol:=finallyexceptionstate.oldflowcontrol+(finallyexceptionstate.newflowcontrol-[fc_inflowcontrol,fc_catching_exceptions]); end; procedure twasmtryfinallynode.pass_generate_code; begin if ts_wasm_no_exceptions in current_settings.targetswitches then pass_generate_code_no_exceptions else if ts_wasm_js_exceptions in current_settings.targetswitches then pass_generate_code_js_exceptions else if ts_wasm_native_exceptions in current_settings.targetswitches then pass_generate_code_native_exceptions else internalerror(2021091704); end; initialization cifnode:=twasmifnode; cwhilerepeatnode:=twasmwhilerepeatnode; craisenode:=twasmraisenode; ctryexceptnode:=twasmtryexceptnode; ctryfinallynode:=twasmtryfinallynode; end.