Browse Source

* improvements to the handling of try..finally in WebAssembly no exceptions
mode - even though exceptions aren't supported in this mode, the finally
section is now executed in case of exit, break or continue

Nikolay Nikolov 3 years ago
parent
commit
848098c63b
2 changed files with 178 additions and 10 deletions
  1. 22 3
      compiler/wasm32/cpupi.pas
  2. 156 7
      compiler/wasm32/nwasmflw.pas

+ 22 - 3
compiler/wasm32/cpupi.pas

@@ -46,7 +46,7 @@ implementation
 
     uses
       systems,verbose,globals,cpubase,tgcpu,aasmdata,aasmcpu,aasmtai,cgexcept,
-      tgobj,paramgr,symconst,symcpu;
+      tgobj,paramgr,symconst,symdef,symtable,symcpu,cgutils,pass_2;
 
 {*****************************************************************************
                      twasmexceptionstatehandler_noexceptions
@@ -54,19 +54,38 @@ implementation
 
     type
       twasmexceptionstatehandler_noexceptions = class(tcgexceptionstatehandler)
+        class procedure get_exception_temps(list:TAsmList;var t:texceptiontemps); override;
+        class procedure unget_exception_temps(list:TAsmList;const t:texceptiontemps); override;
         class procedure new_exception(list:TAsmList;const t:texceptiontemps; const exceptframekind: texceptframekind; out exceptstate: texceptionstate); override;
         class procedure free_exception(list: TAsmList; const t: texceptiontemps; const s: texceptionstate; a: aint; endexceptlabel: tasmlabel; onlyfree:boolean); override;
         class procedure handle_nested_exception(list:TAsmList;var t:texceptiontemps;var entrystate: texceptionstate); override;
       end;
 
+    class procedure twasmexceptionstatehandler_noexceptions.get_exception_temps(list:TAsmList;var t:texceptiontemps);
+      begin
+        if not assigned(exceptionreasontype) then
+          exceptionreasontype:=search_system_proc('fpc_setjmp').returndef;
+        reference_reset(t.envbuf,0,[]);
+        reference_reset(t.jmpbuf,0,[]);
+        tg.gethltemp(list,exceptionreasontype,exceptionreasontype.size,tt_persistent,t.reasonbuf);
+      end;
+
+    class procedure twasmexceptionstatehandler_noexceptions.unget_exception_temps(list:TAsmList;const t:texceptiontemps);
+      begin
+        tg.ungettemp(list,t.reasonbuf);
+      end;
+
     class procedure twasmexceptionstatehandler_noexceptions.new_exception(list:TAsmList;const t:texceptiontemps; const exceptframekind: texceptframekind; out exceptstate: texceptionstate);
       begin
-        list.Concat(tai_comment.Create(strpnew('TODO: new_exception')));
+        exceptstate.exceptionlabel:=nil;
+        exceptstate.oldflowcontrol:=flowcontrol;
+        exceptstate.finallycodelabel:=nil;
+
+        flowcontrol:=[fc_inflowcontrol,fc_catching_exceptions];
       end;
 
     class procedure twasmexceptionstatehandler_noexceptions.free_exception(list: TAsmList; const t: texceptiontemps; const s: texceptionstate; a: aint; endexceptlabel: tasmlabel; onlyfree:boolean);
       begin
-        list.Concat(tai_comment.Create(strpnew('TODO: free_exception')));
       end;
 
     class procedure twasmexceptionstatehandler_noexceptions.handle_nested_exception(list:TAsmList;var t:texceptiontemps;var entrystate: texceptionstate);

+ 156 - 7
compiler/wasm32/nwasmflw.pas

@@ -79,7 +79,7 @@ implementation
     uses
       verbose,globals,systems,globtype,constexp,
       symconst,symdef,symsym,aasmtai,aasmdata,aasmcpu,defutil,defcmp,
-      procinfo,cgbase,pass_1,pass_2,parabase,compinnr,
+      procinfo,cgbase,cgexcept,pass_1,pass_2,parabase,compinnr,
       cpubase,cpuinfo,
       nbas,nld,ncon,ncnv,ncal,ninl,nmem,nadd,
       tgobj,paramgr,
@@ -296,10 +296,99 @@ implementation
 *****************************************************************************}
 
     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;
+
+        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;
 
-        current_asmdata.CurrAsmList.concat(tai_comment.Create(strpnew('TODO: try..finally, try')));
+        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
@@ -309,21 +398,81 @@ implementation
               exit;
           end;
 
-        current_asmdata.CurrAsmList.concat(tai_comment.Create(strpnew('TODO: try..finally, finally')));
+        { 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];
+        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 (flowcontrol-[fc_gotolabel])<>(finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol,fc_catching_exceptions]) then
+          CGMessage(cg_e_control_flow_outside_finally);
         if codegenerror then
           exit;
-        current_asmdata.CurrAsmList.concat(tai_comment.Create(strpnew('TODO: try..finally, end')));
+
+        { 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;