diff --git a/compiler/ncgflw.pas b/compiler/ncgflw.pas index 6f594c19ee..e9adb7a3f8 100644 --- a/compiler/ncgflw.pas +++ b/compiler/ncgflw.pas @@ -529,6 +529,12 @@ implementation reasonbuf : treference; end; + texceptionstate = record + exceptionlabel: TAsmLabel; + oldflowcontrol, + newflowcontrol: tflowcontrol; + end; + procedure get_exception_temps(list:TAsmList;var t:texceptiontemps); begin @@ -546,12 +552,16 @@ implementation end; - procedure new_exception(list:TAsmList;const t:texceptiontemps;exceptlabel:tasmlabel); + procedure new_exception(list:TAsmList;const t:texceptiontemps; out exceptstate: texceptionstate); var paraloc1, paraloc2, paraloc3, pushexceptres, setjmpres: tcgpara; pd: tprocdef; tmpresloc: tlocation; begin + current_asmdata.getjumplabel(exceptstate.exceptionlabel); + exceptstate.oldflowcontrol:=flowcontrol; + flowcontrol:=[fc_inflowcontrol]; + paraloc1.init; paraloc2.init; paraloc3.init; @@ -606,11 +616,19 @@ implementation hlcg.g_exception_reason_save(list,setjmpres.def,ossinttype,tmpresloc.register,t.reasonbuf); { if we get 0 here in the function result register, it means that we longjmp'd back here } - hlcg.a_cmp_const_reg_label(list,setjmpres.def,OC_NE,0,tmpresloc.register,exceptlabel); + hlcg.a_cmp_const_reg_label(list,setjmpres.def,OC_NE,0,tmpresloc.register,exceptstate.exceptionlabel); setjmpres.resetiftemp; end; + procedure emit_except_label(list: TAsmList; var exceptionstate: texceptionstate); + begin + hlcg.a_label(list,exceptionstate.exceptionlabel); + exceptionstate.newflowcontrol:=flowcontrol; + flowcontrol:=exceptionstate.oldflowcontrol; + end; + + procedure free_exception(list:TAsmList;const t:texceptiontemps;a:aint;endexceptlabel:tasmlabel;onlyfree:boolean); var reasonreg: tregister; @@ -643,14 +661,14 @@ implementation { generates code to be executed when another exeception is raised while control is inside except block } - procedure handle_nested_exception(list:TAsmList;const t:texceptiontemps;entrylabel:TAsmLabel); + procedure handle_nested_exception(list:TAsmList;const t:texceptiontemps;var entrystate: texceptionstate); var exitlabel: tasmlabel; begin { don't generate line info for internal cleanup } list.concat(tai_marker.create(mark_NoLineInfoStart)); current_asmdata.getjumplabel(exitlabel); - hlcg.a_label(list,entrylabel); + emit_except_label(current_asmdata.CurrAsmList,entrystate); free_exception(list,t,0,exitlabel,false); { we don't need to save/restore registers here because reraise never } { returns } @@ -671,26 +689,21 @@ implementation exittrylabel, continuetrylabel, breaktrylabel, - doobjectdestroyandreraise, oldCurrExitLabel, oldContinueLabel, oldBreakLabel : tasmlabel; - oldflowcontrol,tryflowcontrol, - exceptflowcontrol : tflowcontrol; destroytemps, excepttemps : texceptiontemps; + trystate,doobjectdestroyandreraisestate: texceptionstate; label errorexit; begin location_reset(location,LOC_VOID,OS_NO); - exceptflowcontrol:=[]; continuetrylabel:=nil; breaktrylabel:=nil; continueexceptlabel:=nil; breakexceptlabel:=nil; - oldflowcontrol:=flowcontrol; - flowcontrol:=[fc_inflowcontrol]; { this can be called recursivly } oldBreakLabel:=nil; oldContinueLabel:=nil; @@ -715,12 +728,11 @@ implementation current_asmdata.getjumplabel(continueexceptlabel); end; - current_asmdata.getjumplabel(exceptlabel); current_asmdata.getjumplabel(endexceptlabel); current_asmdata.getjumplabel(lastonlabel); get_exception_temps(current_asmdata.CurrAsmList,excepttemps); - new_exception(current_asmdata.CurrAsmList,excepttemps,exceptlabel); + new_exception(current_asmdata.CurrAsmList,excepttemps,trystate); { try block } { set control flow labels for the try block } @@ -731,16 +743,14 @@ implementation current_procinfo.CurrBreakLabel:=breaktrylabel; end; - flowcontrol:=[fc_inflowcontrol]; secondpass(left); - tryflowcontrol:=flowcontrol; if codegenerror then goto errorexit; { don't generate line info for internal cleanup } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart)); - hlcg.a_label(current_asmdata.CurrAsmList,exceptlabel); + emit_except_label(current_asmdata.CurrAsmList,trystate); free_exception(current_asmdata.CurrAsmList, excepttemps, 0, endexceptlabel, false); @@ -778,27 +788,30 @@ implementation if not (has_no_code(t1)) then begin - current_asmdata.getjumplabel(doobjectdestroyandreraise); - get_exception_temps(current_asmdata.CurrAsmList,destroytemps); - new_exception(current_asmdata.CurrAsmList,destroytemps,doobjectdestroyandreraise); + new_exception(current_asmdata.CurrAsmList,destroytemps,doobjectdestroyandreraisestate); + { the flowcontrol from the default except-block must be merged + with the flowcontrol flags potentially set by the + on-statements handled above (secondpass(right)), as they are + at the same program level } + flowcontrol:= + flowcontrol+ + doobjectdestroyandreraisestate.oldflowcontrol; + { except block needs line info } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd)); - { here we don't have to reset flowcontrol } - { the default and on flowcontrols are handled equal } secondpass(t1); - exceptflowcontrol:=flowcontrol; - handle_nested_exception(current_asmdata.CurrAsmList,destroytemps,doobjectdestroyandreraise); + handle_nested_exception(current_asmdata.CurrAsmList,destroytemps,doobjectdestroyandreraisestate); unget_exception_temps(current_asmdata.CurrAsmList,destroytemps); hlcg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel); end else begin - exceptflowcontrol:=flowcontrol; + doobjectdestroyandreraisestate.newflowcontrol:=flowcontrol; cleanupobjectstack; hlcg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel); end; @@ -806,10 +819,10 @@ implementation else begin hlcg.g_call_system_proc(current_asmdata.CurrAsmList,'fpc_reraise',[],nil); - exceptflowcontrol:=flowcontrol; + doobjectdestroyandreraisestate.newflowcontrol:=flowcontrol; end; - if fc_exit in exceptflowcontrol then + if fc_exit in doobjectdestroyandreraisestate.newflowcontrol then begin { do some magic for exit in the try block } hlcg.a_label(current_asmdata.CurrAsmList,exitexceptlabel); @@ -821,7 +834,7 @@ implementation hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel); end; - if fc_break in exceptflowcontrol then + if fc_break in doobjectdestroyandreraisestate.newflowcontrol then begin hlcg.a_label(current_asmdata.CurrAsmList,breakexceptlabel); { we must also destroy the address frame which guards } @@ -832,7 +845,7 @@ implementation cg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel); end; - if fc_continue in exceptflowcontrol then + if fc_continue in doobjectdestroyandreraisestate.newflowcontrol then begin hlcg.a_label(current_asmdata.CurrAsmList,continueexceptlabel); { we must also destroy the address frame which guards } @@ -843,7 +856,7 @@ implementation hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel); end; - if fc_exit in tryflowcontrol then + if fc_exit in trystate.newflowcontrol then begin { do some magic for exit in the try block } hlcg.a_label(current_asmdata.CurrAsmList,exittrylabel); @@ -852,7 +865,7 @@ implementation hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel); end; - if fc_break in tryflowcontrol then + if fc_break in trystate.newflowcontrol then begin hlcg.a_label(current_asmdata.CurrAsmList,breaktrylabel); hlcg.g_call_system_proc(current_asmdata.CurrAsmList,'fpc_popaddrstack',[],nil); @@ -860,7 +873,7 @@ implementation cg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel); end; - if fc_continue in tryflowcontrol then + if fc_continue in trystate.newflowcontrol then begin hlcg.a_label(current_asmdata.CurrAsmList,continuetrylabel); hlcg.g_call_system_proc(current_asmdata.CurrAsmList,'fpc_popaddrstack',[],nil); @@ -886,8 +899,8 @@ implementation end; { return all used control flow statements } - flowcontrol:=oldflowcontrol+(exceptflowcontrol + - tryflowcontrol - [fc_inflowcontrol]); + flowcontrol:=trystate.oldflowcontrol+(doobjectdestroyandreraisestate.newflowcontrol + + trystate.newflowcontrol - [fc_inflowcontrol]); end; @@ -899,9 +912,8 @@ implementation breakonlabel, oldCurrExitLabel, oldContinueLabel, - doobjectdestroyandreraise, oldBreakLabel : tasmlabel; - oldflowcontrol : tflowcontrol; + doobjectdestroyandreraisestate: texceptionstate; excepttemps : texceptiontemps; href2: treference; paraloc1 : tcgpara; @@ -919,8 +931,6 @@ implementation breakonlabel:=nil; exitonlabel:=nil; - oldflowcontrol:=flowcontrol; - flowcontrol:=[fc_inflowcontrol]; current_asmdata.getjumplabel(nextonlabel); otherunit:=findunitsymtable(excepttype.owner).moduleid<>findunitsymtable(current_procinfo.procdef.owner).moduleid; @@ -959,12 +969,10 @@ implementation end; { in the case that another exception is risen - we've to destroy the old one } - current_asmdata.getjumplabel(doobjectdestroyandreraise); - - { call setjmp, and jump to finally label on non-zero result } + we've to destroy the old one: + call setjmp, and jump to finally label on non-zero result } get_exception_temps(current_asmdata.CurrAsmList,excepttemps); - new_exception(current_asmdata.CurrAsmList,excepttemps,doobjectdestroyandreraise); + new_exception(current_asmdata.CurrAsmList,excepttemps,doobjectdestroyandreraisestate); oldBreakLabel:=nil; oldContinueLabel:=nil; @@ -986,7 +994,7 @@ implementation secondpass(right); end; - handle_nested_exception(current_asmdata.CurrAsmList,excepttemps,doobjectdestroyandreraise); + handle_nested_exception(current_asmdata.CurrAsmList,excepttemps,doobjectdestroyandreraisestate); { clear some stuff } if assigned(exceptvarsym) then @@ -999,21 +1007,21 @@ implementation if assigned(right) then begin { special handling for control flow instructions } - if fc_exit in flowcontrol then + if fc_exit in doobjectdestroyandreraisestate.newflowcontrol then begin { the address and object pop does secondtryexcept } cg.a_label(current_asmdata.CurrAsmList,exitonlabel); cg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel); end; - if fc_break in flowcontrol then + if fc_break in doobjectdestroyandreraisestate.newflowcontrol then begin { the address and object pop does secondtryexcept } cg.a_label(current_asmdata.CurrAsmList,breakonlabel); cg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel); end; - if fc_continue in flowcontrol then + if fc_continue in doobjectdestroyandreraisestate.newflowcontrol then begin { the address and object pop does secondtryexcept } cg.a_label(current_asmdata.CurrAsmList,continueonlabel); @@ -1030,7 +1038,7 @@ implementation unget_exception_temps(current_asmdata.CurrAsmList,excepttemps); cg.a_label(current_asmdata.CurrAsmList,nextonlabel); - flowcontrol:=oldflowcontrol+(flowcontrol-[fc_inflowcontrol]); + flowcontrol:=doobjectdestroyandreraisestate.oldflowcontrol+(doobjectdestroyandreraisestate.newflowcontrol-[fc_inflowcontrol]); paraloc1.done; current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd)); @@ -1071,7 +1079,6 @@ implementation procedure tcgtryfinallynode.pass_generate_code; var - finallylabel, endfinallylabel, exitfinallylabel, continuefinallylabel, @@ -1079,28 +1086,28 @@ implementation oldCurrExitLabel, oldContinueLabel, oldBreakLabel : tasmlabel; - oldflowcontrol,tryflowcontrol : tflowcontrol; + finallyexceptionstate: texceptionstate; + prefinallyflowcontrol : tflowcontrol; excepttemps : texceptiontemps; reasonreg : tregister; begin location_reset(location,LOC_VOID,OS_NO); - tryflowcontrol:=[]; oldBreakLabel:=nil; oldContinueLabel:=nil; continuefinallylabel:=nil; breakfinallylabel:=nil; - { check if child nodes do a break/continue/exit } - oldflowcontrol:=flowcontrol; - flowcontrol:=[fc_inflowcontrol]; - current_asmdata.getjumplabel(finallylabel); current_asmdata.getjumplabel(endfinallylabel); + { call setjmp, and jump to finally label on non-zero result } + get_exception_temps(current_asmdata.CurrAsmList,excepttemps); + new_exception(current_asmdata.CurrAsmList,excepttemps,finallyexceptionstate); + { the finally block must catch break, continue and exit } { statements } oldCurrExitLabel:=current_procinfo.CurrExitLabel; if implicitframe then - exitfinallylabel:=finallylabel + exitfinallylabel:=finallyexceptionstate.exceptionlabel else current_asmdata.getjumplabel(exitfinallylabel); current_procinfo.CurrExitLabel:=exitfinallylabel; @@ -1110,8 +1117,8 @@ implementation oldBreakLabel:=current_procinfo.CurrBreakLabel; if implicitframe then begin - breakfinallylabel:=finallylabel; - continuefinallylabel:=finallylabel; + breakfinallylabel:=finallyexceptionstate.exceptionlabel; + continuefinallylabel:=finallyexceptionstate.exceptionlabel; end else begin @@ -1122,15 +1129,10 @@ implementation current_procinfo.CurrBreakLabel:=breakfinallylabel; end; - { call setjmp, and jump to finally label on non-zero result } - get_exception_temps(current_asmdata.CurrAsmList,excepttemps); - new_exception(current_asmdata.CurrAsmList,excepttemps,finallylabel); - { try code } if assigned(left) then begin secondpass(left); - tryflowcontrol:=flowcontrol; if codegenerror then exit; end; @@ -1138,19 +1140,22 @@ implementation { don't generate line info for internal cleanup } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart)); - hlcg.a_label(current_asmdata.CurrAsmList,finallylabel); + emit_except_label(current_asmdata.CurrAsmList,finallyexceptionstate); { just free the frame information } - free_exception(current_asmdata.CurrAsmList,excepttemps,1,finallylabel,true); + free_exception(current_asmdata.CurrAsmList,excepttemps,1,finallyexceptionstate.exceptionlabel,true); { end cleanup } current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd)); - { finally code } - flowcontrol:=[fc_inflowcontrol]; + { 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]; secondpass(right); { goto is allowed if it stays inside the finally block, this is checked using the exception block number } - if (flowcontrol-[fc_gotolabel])<>[fc_inflowcontrol] then + if (flowcontrol-[fc_gotolabel])<>(finallyexceptionstate.oldflowcontrol*[fc_inflowcontrol]) then CGMessage(cg_e_control_flow_outside_finally); if codegenerror then exit; @@ -1164,7 +1169,8 @@ implementation if implicitframe then begin hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,osuinttype,OC_EQ,0,reasonreg,endfinallylabel); - { finally code only needed to be executed on exception } + { finally code only needed to be executed on exception (-> in + if-branch -> fc_inflowcontrol) } flowcontrol:=[fc_inflowcontrol]; secondpass(t1); if flowcontrol<>[fc_inflowcontrol] then @@ -1180,34 +1186,34 @@ implementation else begin hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,osuinttype,OC_EQ,0,reasonreg,endfinallylabel); - if fc_exit in tryflowcontrol then + if fc_exit in finallyexceptionstate.newflowcontrol then hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,osuinttype,OC_EQ,2,reasonreg,oldCurrExitLabel); - if fc_break in tryflowcontrol then + if fc_break in finallyexceptionstate.newflowcontrol then hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,osuinttype,OC_EQ,3,reasonreg,oldBreakLabel); - if fc_continue in tryflowcontrol then + if fc_continue in finallyexceptionstate.newflowcontrol then hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,osuinttype,OC_EQ,4,reasonreg,oldContinueLabel); hlcg.g_call_system_proc(current_asmdata.CurrAsmList,'fpc_reraise',[],nil); { do some magic for exit,break,continue in the try block } - if fc_exit in tryflowcontrol then + if fc_exit in finallyexceptionstate.newflowcontrol then begin hlcg.a_label(current_asmdata.CurrAsmList,exitfinallylabel); hlcg.g_exception_reason_discard(current_asmdata.CurrAsmList,osuinttype,excepttemps.reasonbuf); hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,osuinttype,2,excepttemps.reasonbuf); - hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel); + hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallyexceptionstate.exceptionlabel); end; - if fc_break in tryflowcontrol then + if fc_break in finallyexceptionstate.newflowcontrol then begin hlcg.a_label(current_asmdata.CurrAsmList,breakfinallylabel); hlcg.g_exception_reason_discard(current_asmdata.CurrAsmList,osuinttype,excepttemps.reasonbuf); hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,osuinttype,3,excepttemps.reasonbuf); - hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel); + hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallyexceptionstate.exceptionlabel); end; - if fc_continue in tryflowcontrol then + if fc_continue in finallyexceptionstate.newflowcontrol then begin hlcg.a_label(current_asmdata.CurrAsmList,continuefinallylabel); hlcg.g_exception_reason_discard(current_asmdata.CurrAsmList,osuinttype,excepttemps.reasonbuf); hlcg.g_exception_reason_save_const(current_asmdata.CurrAsmList,osuinttype,4,excepttemps.reasonbuf); - hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel); + hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallyexceptionstate.exceptionlabel); end; end; unget_exception_temps(current_asmdata.CurrAsmList,excepttemps); @@ -1222,7 +1228,7 @@ implementation current_procinfo.CurrContinueLabel:=oldContinueLabel; current_procinfo.CurrBreakLabel:=oldBreakLabel; end; - flowcontrol:=oldflowcontrol+(tryflowcontrol-[fc_inflowcontrol]); + flowcontrol:=finallyexceptionstate.oldflowcontrol+(finallyexceptionstate.newflowcontrol-[fc_inflowcontrol]); end;