|
@@ -0,0 +1,685 @@
|
|
|
+{
|
|
|
+ Copyright (c) 2011 by Free Pascal development team
|
|
|
+
|
|
|
+ Generate Win32-specific exception handling code
|
|
|
+
|
|
|
+ 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 n386flw;
|
|
|
+
|
|
|
+{$i fpcdefs.inc}
|
|
|
+
|
|
|
+interface
|
|
|
+
|
|
|
+ uses
|
|
|
+ node,nflw,ncgflw,psub;
|
|
|
+
|
|
|
+ type
|
|
|
+ ti386raisenode=class(tcgraisenode)
|
|
|
+ function pass_1 : tnode;override;
|
|
|
+ end;
|
|
|
+
|
|
|
+ ti386onnode=class(tcgonnode)
|
|
|
+ procedure pass_generate_code;override;
|
|
|
+ end;
|
|
|
+
|
|
|
+ ti386tryexceptnode=class(tcgtryexceptnode)
|
|
|
+ procedure pass_generate_code;override;
|
|
|
+ end;
|
|
|
+
|
|
|
+ ti386tryfinallynode=class(tcgtryfinallynode)
|
|
|
+ finalizepi: tcgprocinfo;
|
|
|
+ constructor create(l,r:TNode);override;
|
|
|
+ constructor create_implicit(l,r,_t1:TNode);override;
|
|
|
+ function pass_1: tnode;override;
|
|
|
+ function simplify(forinline: boolean): tnode;override;
|
|
|
+ procedure pass_generate_code;override;
|
|
|
+ end;
|
|
|
+
|
|
|
+implementation
|
|
|
+
|
|
|
+ uses
|
|
|
+ cutils,globtype,globals,verbose,systems,
|
|
|
+ nbas,ncal,nmem,nutils,
|
|
|
+ symconst,symbase,symtable,symsym,symdef,
|
|
|
+ cgbase,cgobj,cgcpu,cgutils,tgobj,
|
|
|
+ cpubase,htypechk,
|
|
|
+ parabase,paramgr,pdecsub,pass_1,pass_2,ncgutil,cga,
|
|
|
+ aasmbase,aasmtai,aasmdata,aasmcpu,procinfo,cpupi;
|
|
|
+
|
|
|
+ var
|
|
|
+ endexceptlabel: tasmlabel;
|
|
|
+
|
|
|
+
|
|
|
+{ ti386raisenode }
|
|
|
+
|
|
|
+function ti386raisenode.pass_1 : tnode;
|
|
|
+ var
|
|
|
+ statements : tstatementnode;
|
|
|
+ raisenode : tcallnode;
|
|
|
+ begin
|
|
|
+ { difference from generic code is that address stack is not popped on reraise }
|
|
|
+ if (target_info.system<>system_i386_win32) or assigned(left) then
|
|
|
+ result:=inherited pass_1
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ result:=internalstatements(statements);
|
|
|
+ raisenode:=ccallnode.createintern('fpc_reraise',nil);
|
|
|
+ include(raisenode.callnodeflags,cnf_call_never_returns);
|
|
|
+ addstatement(statements,raisenode);
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+{ ti386onnode }
|
|
|
+
|
|
|
+procedure ti386onnode.pass_generate_code;
|
|
|
+ var
|
|
|
+ oldflowcontrol : tflowcontrol;
|
|
|
+ exceptvarsym : tlocalvarsym;
|
|
|
+ begin
|
|
|
+ if (target_info.system<>system_i386_win32) then
|
|
|
+ begin
|
|
|
+ inherited pass_generate_code;
|
|
|
+ exit;
|
|
|
+ end;
|
|
|
+
|
|
|
+ location_reset(location,LOC_VOID,OS_NO);
|
|
|
+
|
|
|
+ oldflowcontrol:=flowcontrol;
|
|
|
+ flowcontrol:=flowcontrol*[fc_unwind]+[fc_inflowcontrol];
|
|
|
+
|
|
|
+ { RTL will put exceptobject into EAX when jumping here }
|
|
|
+ cg.a_reg_alloc(current_asmdata.CurrAsmList,NR_FUNCTION_RESULT_REG);
|
|
|
+
|
|
|
+ { Retrieve exception variable }
|
|
|
+ if assigned(excepTSymtable) then
|
|
|
+ exceptvarsym:=tlocalvarsym(excepTSymtable.SymList[0])
|
|
|
+ else
|
|
|
+ exceptvarsym:=nil;
|
|
|
+
|
|
|
+ if assigned(exceptvarsym) then
|
|
|
+ begin
|
|
|
+ exceptvarsym.localloc.loc:=LOC_REFERENCE;
|
|
|
+ exceptvarsym.localloc.size:=OS_ADDR;
|
|
|
+ tg.GetLocal(current_asmdata.CurrAsmList,sizeof(pint),voidpointertype,exceptvarsym.localloc.reference);
|
|
|
+ cg.a_load_reg_ref(current_asmdata.CurrAsmList,OS_ADDR,OS_ADDR,NR_FUNCTION_RESULT_REG,exceptvarsym.localloc.reference);
|
|
|
+ end;
|
|
|
+ cg.a_reg_dealloc(current_asmdata.CurrAsmList,NR_FUNCTION_RESULT_REG);
|
|
|
+
|
|
|
+ if assigned(right) then
|
|
|
+ secondpass(right);
|
|
|
+
|
|
|
+ { deallocate exception symbol }
|
|
|
+ if assigned(exceptvarsym) then
|
|
|
+ begin
|
|
|
+ tg.UngetLocal(current_asmdata.CurrAsmList,exceptvarsym.localloc.reference);
|
|
|
+ exceptvarsym.localloc.loc:=LOC_INVALID;
|
|
|
+ end;
|
|
|
+ cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION');
|
|
|
+ cg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
|
|
|
+
|
|
|
+ flowcontrol:=oldflowcontrol+(flowcontrol-[fc_inflowcontrol]);
|
|
|
+ end;
|
|
|
+
|
|
|
+{ ti386tryfinallynode }
|
|
|
+
|
|
|
+function reset_regvars(var n: tnode; arg: pointer): foreachnoderesult;
|
|
|
+ begin
|
|
|
+ case n.nodetype of
|
|
|
+ temprefn:
|
|
|
+ make_not_regable(n,[]);
|
|
|
+ calln:
|
|
|
+ include(tprocinfo(arg).flags,pi_do_call);
|
|
|
+ end;
|
|
|
+ result:=fen_true;
|
|
|
+ end;
|
|
|
+
|
|
|
+function copy_parasize(var n: tnode; arg: pointer): foreachnoderesult;
|
|
|
+ begin
|
|
|
+ case n.nodetype of
|
|
|
+ calln:
|
|
|
+ tcgprocinfo(arg).allocate_push_parasize(tcallnode(n).pushed_parasize);
|
|
|
+ end;
|
|
|
+ result:=fen_true;
|
|
|
+ end;
|
|
|
+
|
|
|
+constructor ti386tryfinallynode.create(l, r: TNode);
|
|
|
+ begin
|
|
|
+ inherited create(l,r);
|
|
|
+ if (target_info.system<>system_i386_win32) or (
|
|
|
+ { Don't create child procedures for generic methods, their nested-like
|
|
|
+ behavior causes compilation errors because real nested procedures
|
|
|
+ aren't allowed for generics. Not creating them doesn't harm because
|
|
|
+ generic node tree is discarded without generating code. }
|
|
|
+ assigned(current_procinfo.procdef.struct) and
|
|
|
+ (df_generic in current_procinfo.procdef.struct.defoptions)
|
|
|
+ ) then
|
|
|
+ exit;
|
|
|
+ finalizepi:=tcgprocinfo(cprocinfo.create(current_procinfo));
|
|
|
+ finalizepi.force_nested;
|
|
|
+ finalizepi.procdef:=create_finalizer_procdef;
|
|
|
+ finalizepi.entrypos:=r.fileinfo;
|
|
|
+ finalizepi.entryswitches:=r.localswitches;
|
|
|
+ finalizepi.exitpos:=current_filepos; // last_endtoken_pos?
|
|
|
+ finalizepi.exitswitches:=current_settings.localswitches;
|
|
|
+ { Regvar optimization for symbols is suppressed when using exceptions, but
|
|
|
+ temps may be still placed into registers. This must be fixed. }
|
|
|
+ foreachnodestatic(r,@reset_regvars,finalizepi);
|
|
|
+ include(finalizepi.flags,pi_has_assembler_block);
|
|
|
+ include(finalizepi.flags,pi_do_call);
|
|
|
+ include(finalizepi.flags,pi_uses_exceptions);
|
|
|
+ end;
|
|
|
+
|
|
|
+constructor ti386tryfinallynode.create_implicit(l, r, _t1: TNode);
|
|
|
+ begin
|
|
|
+ inherited create_implicit(l, r, _t1);
|
|
|
+ if (target_info.system<>system_i386_win32) then
|
|
|
+ exit;
|
|
|
+
|
|
|
+ { safecall procedures can handle implicit finalization as part of "except" flow }
|
|
|
+ if implicitframe and (current_procinfo.procdef.proccalloption=pocall_safecall) then
|
|
|
+ exit;
|
|
|
+
|
|
|
+ if assigned(current_procinfo.procdef.struct) and
|
|
|
+ (df_generic in current_procinfo.procdef.struct.defoptions) then
|
|
|
+ InternalError(2013012501);
|
|
|
+
|
|
|
+ finalizepi:=tcgprocinfo(cprocinfo.create(current_procinfo));
|
|
|
+ finalizepi.force_nested;
|
|
|
+ finalizepi.procdef:=create_finalizer_procdef;
|
|
|
+ finalizepi.entrypos:=current_filepos;
|
|
|
+ finalizepi.exitpos:=current_filepos; // last_endtoken_pos?
|
|
|
+ finalizepi.entryswitches:=r.localswitches;
|
|
|
+ finalizepi.exitswitches:=current_settings.localswitches;
|
|
|
+ include(finalizepi.flags,pi_has_assembler_block);
|
|
|
+ include(finalizepi.flags,pi_do_call);
|
|
|
+ include(finalizepi.flags,pi_uses_exceptions);
|
|
|
+ end;
|
|
|
+
|
|
|
+
|
|
|
+function ti386tryfinallynode.pass_1: tnode;
|
|
|
+ var
|
|
|
+ selfsym: tparavarsym;
|
|
|
+ begin
|
|
|
+ result:=inherited pass_1;
|
|
|
+ if (target_info.system=system_i386_win32) then
|
|
|
+ begin
|
|
|
+ { safecall method will access 'self' from except block -> make it non-regable }
|
|
|
+ if implicitframe and (current_procinfo.procdef.proccalloption=pocall_safecall) and
|
|
|
+ is_class(current_procinfo.procdef.struct) then
|
|
|
+ begin
|
|
|
+ selfsym:=tparavarsym(current_procinfo.procdef.parast.Find('self'));
|
|
|
+ if (selfsym=nil) or (selfsym.typ<>paravarsym) then
|
|
|
+ InternalError(2011123101);
|
|
|
+ selfsym.varregable:=vr_none;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+
|
|
|
+function ti386tryfinallynode.simplify(forinline: boolean): tnode;
|
|
|
+ begin
|
|
|
+ result:=inherited simplify(forinline);
|
|
|
+ if (target_info.system<>system_i386_win32) then
|
|
|
+ exit;
|
|
|
+
|
|
|
+ if (result=nil) and assigned(finalizepi) then
|
|
|
+ begin
|
|
|
+ finalizepi.code:=right;
|
|
|
+ foreachnodestatic(right,@copy_parasize,finalizepi);
|
|
|
+ right:=ccallnode.create(nil,tprocsym(finalizepi.procdef.procsym),nil,nil,[]);
|
|
|
+ firstpass(right);
|
|
|
+ { For implicit frames, no actual code is available at this time,
|
|
|
+ it is added later in assembler form. So store the nested procinfo
|
|
|
+ for later use. }
|
|
|
+ if implicitframe then
|
|
|
+ begin
|
|
|
+ current_procinfo.finalize_procinfo:=finalizepi;
|
|
|
+ { don't leave dangling pointer }
|
|
|
+ tcgprocinfo(current_procinfo).final_asmnode:=nil;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+
|
|
|
+procedure emit_scope_start(handler,data: TAsmSymbol);
|
|
|
+ var
|
|
|
+ href: treference;
|
|
|
+ hreg: tregister;
|
|
|
+ begin
|
|
|
+ hreg:=cg.getintregister(current_asmdata.CurrAsmList,OS_ADDR);
|
|
|
+ reference_reset_base(href,hreg,0,sizeof(pint));
|
|
|
+ href.segment:=NR_FS;
|
|
|
+ emit_reg_reg(A_XOR,S_L,hreg,hreg);
|
|
|
+ emit_sym(A_PUSH,S_L,data);
|
|
|
+ emit_reg(A_PUSH,S_L,NR_FRAME_POINTER_REG);
|
|
|
+ emit_sym(A_PUSH,S_L,handler);
|
|
|
+ emit_ref(A_PUSH,S_L,href);
|
|
|
+ emit_reg_ref(A_MOV,S_L,NR_ESP,href);
|
|
|
+ end;
|
|
|
+
|
|
|
+procedure emit_scope_end;
|
|
|
+ var
|
|
|
+ href: treference;
|
|
|
+ hreg,hreg2: tregister;
|
|
|
+ begin
|
|
|
+ hreg:=cg.getintregister(current_asmdata.CurrAsmList,OS_ADDR);
|
|
|
+ hreg2:=cg.getintregister(current_asmdata.CurrAsmList,OS_ADDR);
|
|
|
+ reference_reset_base(href,hreg,0,sizeof(pint));
|
|
|
+ href.segment:=NR_FS;
|
|
|
+ emit_reg_reg(A_XOR,S_L,hreg,hreg);
|
|
|
+ emit_reg(A_POP,S_L,hreg2);
|
|
|
+ emit_const_reg(A_ADD,S_L,3*sizeof(pint),NR_ESP);
|
|
|
+ emit_reg_ref(A_MOV,S_L,hreg2,href);
|
|
|
+ end;
|
|
|
+
|
|
|
+procedure ti386tryfinallynode.pass_generate_code;
|
|
|
+ var
|
|
|
+ finallylabel,
|
|
|
+ exceptlabel,
|
|
|
+ safecalllabel,
|
|
|
+ endfinallylabel,
|
|
|
+ exitfinallylabel,
|
|
|
+ continuefinallylabel,
|
|
|
+ breakfinallylabel,
|
|
|
+ oldCurrExitLabel,
|
|
|
+ oldContinueLabel,
|
|
|
+ oldBreakLabel : tasmlabel;
|
|
|
+ oldflowcontrol,tryflowcontrol : tflowcontrol;
|
|
|
+ is_safecall: boolean;
|
|
|
+ hreg: tregister;
|
|
|
+ begin
|
|
|
+ if (target_info.system<>system_i386_win32) then
|
|
|
+ begin
|
|
|
+ inherited pass_generate_code;
|
|
|
+ exit;
|
|
|
+ end;
|
|
|
+ location_reset(location,LOC_VOID,OS_NO);
|
|
|
+ tryflowcontrol:=[];
|
|
|
+ oldBreakLabel:=nil;
|
|
|
+ oldContinueLabel:=nil;
|
|
|
+ continuefinallylabel:=nil;
|
|
|
+ breakfinallylabel:=nil;
|
|
|
+ exceptlabel:=nil;
|
|
|
+ safecalllabel:=nil;
|
|
|
+ is_safecall:=implicitframe and (current_procinfo.procdef.proccalloption=pocall_safecall);
|
|
|
+
|
|
|
+ { check if child nodes do a break/continue/exit }
|
|
|
+ oldflowcontrol:=flowcontrol;
|
|
|
+ flowcontrol:=[fc_inflowcontrol];
|
|
|
+ current_asmdata.getjumplabel(finallylabel);
|
|
|
+ current_asmdata.getjumplabel(endfinallylabel);
|
|
|
+
|
|
|
+ { the finally block must catch break, continue and exit }
|
|
|
+ { statements }
|
|
|
+ oldCurrExitLabel:=current_procinfo.CurrExitLabel;
|
|
|
+ if implicitframe then
|
|
|
+ exitfinallylabel:=finallylabel
|
|
|
+ else
|
|
|
+ current_asmdata.getjumplabel(exitfinallylabel);
|
|
|
+ current_procinfo.CurrExitLabel:=exitfinallylabel;
|
|
|
+ if assigned(current_procinfo.CurrBreakLabel) then
|
|
|
+ begin
|
|
|
+ oldContinueLabel:=current_procinfo.CurrContinueLabel;
|
|
|
+ oldBreakLabel:=current_procinfo.CurrBreakLabel;
|
|
|
+ if implicitframe then
|
|
|
+ begin
|
|
|
+ breakfinallylabel:=finallylabel;
|
|
|
+ continuefinallylabel:=finallylabel;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ current_asmdata.getjumplabel(breakfinallylabel);
|
|
|
+ current_asmdata.getjumplabel(continuefinallylabel);
|
|
|
+ end;
|
|
|
+ current_procinfo.CurrContinueLabel:=continuefinallylabel;
|
|
|
+ current_procinfo.CurrBreakLabel:=breakfinallylabel;
|
|
|
+ end;
|
|
|
+
|
|
|
+ { Start of scope }
|
|
|
+ if is_safecall then
|
|
|
+ begin
|
|
|
+ with cg.rg[R_INTREGISTER] do
|
|
|
+ used_in_proc:=used_in_proc+[RS_EBX,RS_ESI,RS_EDI];
|
|
|
+
|
|
|
+ current_asmdata.getjumplabel(exceptlabel);
|
|
|
+ emit_scope_start(
|
|
|
+ current_asmdata.RefAsmSymbol('__FPC_except_safecall'),
|
|
|
+ exceptlabel
|
|
|
+ );
|
|
|
+ end
|
|
|
+ else
|
|
|
+ emit_scope_start(
|
|
|
+ current_asmdata.RefAsmSymbol('__FPC_finally_handler'),
|
|
|
+ current_asmdata.RefAsmSymbol(finalizepi.procdef.mangledname)
|
|
|
+ );
|
|
|
+
|
|
|
+ { try code }
|
|
|
+ if assigned(left) then
|
|
|
+ begin
|
|
|
+ secondpass(left);
|
|
|
+ tryflowcontrol:=flowcontrol;
|
|
|
+ if codegenerror then
|
|
|
+ exit;
|
|
|
+ end;
|
|
|
+
|
|
|
+ { don't generate line info for internal cleanup }
|
|
|
+ current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
|
|
|
+
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,finallylabel);
|
|
|
+ emit_scope_end;
|
|
|
+ if is_safecall then
|
|
|
+ begin
|
|
|
+ current_asmdata.getjumplabel(safecalllabel);
|
|
|
+ hreg:=cg.GetIntRegister(current_asmdata.CurrAsmList,OS_INT);
|
|
|
+ cg.a_load_const_reg(current_asmdata.CurrAsmList,OS_INT,0,hreg);
|
|
|
+ cg.a_jmp_always(current_asmdata.CurrAsmList,safecalllabel);
|
|
|
+ { RTL handler will jump here on exception }
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,exceptlabel);
|
|
|
+ handle_safecall_exception;
|
|
|
+ cg.a_load_reg_reg(current_asmdata.CurrAsmList,OS_INT,OS_INT,NR_FUNCTION_RESULT_REG,hreg);
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,safecalllabel);
|
|
|
+ end;
|
|
|
+
|
|
|
+ { end cleanup }
|
|
|
+ current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
|
|
|
+
|
|
|
+ { generate finally code as a separate procedure }
|
|
|
+ { !!! this resets flowcontrol, how to check flow away? }
|
|
|
+ if not implicitframe then
|
|
|
+ tcgprocinfo(current_procinfo).generate_exceptfilter(finalizepi);
|
|
|
+
|
|
|
+ flowcontrol:=[fc_inflowcontrol];
|
|
|
+ { right is a call to finalizer procedure }
|
|
|
+ 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
|
|
|
+ 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 not implicitframe then
|
|
|
+ begin
|
|
|
+ if tryflowcontrol*[fc_exit,fc_break,fc_continue]<>[] then
|
|
|
+ cg.a_jmp_always(current_asmdata.CurrAsmList,endfinallylabel);
|
|
|
+ { do some magic for exit,break,continue in the try block }
|
|
|
+ if fc_exit in tryflowcontrol then
|
|
|
+ begin
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,exitfinallylabel);
|
|
|
+ cg.g_call(current_asmdata.CurrAsmList,'_FPC_leave');
|
|
|
+ cg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel);
|
|
|
+ end;
|
|
|
+ if fc_break in tryflowcontrol then
|
|
|
+ begin
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,breakfinallylabel);
|
|
|
+ cg.g_call(current_asmdata.CurrAsmList,'_FPC_leave');
|
|
|
+ cg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel);
|
|
|
+ end;
|
|
|
+ if fc_continue in tryflowcontrol then
|
|
|
+ begin
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,continuefinallylabel);
|
|
|
+ cg.g_call(current_asmdata.CurrAsmList,'_FPC_leave');
|
|
|
+ cg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ if is_safecall then
|
|
|
+ cg.a_load_reg_reg(current_asmdata.CurrAsmList,OS_INT,OS_INT,hreg,NR_FUNCTION_RETURN_REG);
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,endfinallylabel);
|
|
|
+
|
|
|
+ { end cleanup }
|
|
|
+ current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
|
|
|
+
|
|
|
+ current_procinfo.CurrExitLabel:=oldCurrExitLabel;
|
|
|
+ if assigned(current_procinfo.CurrBreakLabel) then
|
|
|
+ begin
|
|
|
+ current_procinfo.CurrContinueLabel:=oldContinueLabel;
|
|
|
+ current_procinfo.CurrBreakLabel:=oldBreakLabel;
|
|
|
+ end;
|
|
|
+ flowcontrol:=oldflowcontrol+(tryflowcontrol-[fc_inflowcontrol]);
|
|
|
+ end;
|
|
|
+
|
|
|
+{ ti386tryexceptnode }
|
|
|
+
|
|
|
+procedure ti386tryexceptnode.pass_generate_code;
|
|
|
+ var
|
|
|
+ exceptlabel,oldendexceptlabel,
|
|
|
+ lastonlabel,
|
|
|
+ exitexceptlabel,
|
|
|
+ continueexceptlabel,
|
|
|
+ breakexceptlabel,
|
|
|
+ exittrylabel,
|
|
|
+ continuetrylabel,
|
|
|
+ breaktrylabel,
|
|
|
+ oldCurrExitLabel,
|
|
|
+ oldContinueLabel,
|
|
|
+ oldBreakLabel : tasmlabel;
|
|
|
+ onlabel,
|
|
|
+ filterlabel: tasmlabel;
|
|
|
+ oldflowcontrol,tryflowcontrol,
|
|
|
+ exceptflowcontrol : tflowcontrol;
|
|
|
+ hnode : tnode;
|
|
|
+ hlist : tasmlist;
|
|
|
+ onnodecount : tai_const;
|
|
|
+ label
|
|
|
+ errorexit;
|
|
|
+ begin
|
|
|
+ if (target_info.system<>system_i386_win32) then
|
|
|
+ begin
|
|
|
+ inherited pass_generate_code;
|
|
|
+ exit;
|
|
|
+ end;
|
|
|
+ location_reset(location,LOC_VOID,OS_NO);
|
|
|
+
|
|
|
+ oldflowcontrol:=flowcontrol;
|
|
|
+ flowcontrol:=[fc_inflowcontrol];
|
|
|
+ { this can be called recursivly }
|
|
|
+ oldBreakLabel:=nil;
|
|
|
+ oldContinueLabel:=nil;
|
|
|
+ oldendexceptlabel:=endexceptlabel;
|
|
|
+
|
|
|
+ { Win32 SEH unwinding does not preserve registers. Indicate that they are
|
|
|
+ going to be destroyed. }
|
|
|
+ cg.alloccpuregisters(current_asmdata.CurrAsmList,R_INTREGISTER,[RS_EAX,RS_EBX,RS_ECX,RS_EDX,RS_ESI,RS_EDI]);
|
|
|
+ cg.dealloccpuregisters(current_asmdata.CurrAsmList,R_INTREGISTER,[RS_EAX,RS_EBX,RS_ECX,RS_EDX,RS_ESI,RS_EDI]);
|
|
|
+
|
|
|
+ { save the old labels for control flow statements }
|
|
|
+ oldCurrExitLabel:=current_procinfo.CurrExitLabel;
|
|
|
+ if assigned(current_procinfo.CurrBreakLabel) then
|
|
|
+ begin
|
|
|
+ oldContinueLabel:=current_procinfo.CurrContinueLabel;
|
|
|
+ oldBreakLabel:=current_procinfo.CurrBreakLabel;
|
|
|
+ end;
|
|
|
+
|
|
|
+ { get new labels for the control flow statements }
|
|
|
+ current_asmdata.getjumplabel(exittrylabel);
|
|
|
+ current_asmdata.getjumplabel(exitexceptlabel);
|
|
|
+ if assigned(current_procinfo.CurrBreakLabel) then
|
|
|
+ begin
|
|
|
+ current_asmdata.getjumplabel(breaktrylabel);
|
|
|
+ current_asmdata.getjumplabel(continuetrylabel);
|
|
|
+ current_asmdata.getjumplabel(breakexceptlabel);
|
|
|
+ current_asmdata.getjumplabel(continueexceptlabel);
|
|
|
+ end;
|
|
|
+
|
|
|
+ current_asmdata.getjumplabel(exceptlabel);
|
|
|
+ current_asmdata.getjumplabel(endexceptlabel);
|
|
|
+ current_asmdata.getjumplabel(lastonlabel);
|
|
|
+ filterlabel:=nil;
|
|
|
+
|
|
|
+ { start of scope }
|
|
|
+ if assigned(right) then
|
|
|
+ begin
|
|
|
+ current_asmdata.getdatalabel(filterlabel);
|
|
|
+ emit_scope_start(
|
|
|
+ current_asmdata.RefAsmSymbol('__FPC_on_handler'),
|
|
|
+ filterlabel);
|
|
|
+ end
|
|
|
+ else
|
|
|
+ emit_scope_start(
|
|
|
+ current_asmdata.RefAsmSymbol('__FPC_except_handler'),
|
|
|
+ exceptlabel);
|
|
|
+
|
|
|
+ { set control flow labels for the try block }
|
|
|
+ current_procinfo.CurrExitLabel:=exittrylabel;
|
|
|
+ if assigned(oldBreakLabel) then
|
|
|
+ begin
|
|
|
+ current_procinfo.CurrContinueLabel:=continuetrylabel;
|
|
|
+ current_procinfo.CurrBreakLabel:=breaktrylabel;
|
|
|
+ end;
|
|
|
+
|
|
|
+ secondpass(left);
|
|
|
+ tryflowcontrol:=flowcontrol;
|
|
|
+ if codegenerror then
|
|
|
+ goto errorexit;
|
|
|
+
|
|
|
+ emit_scope_end;
|
|
|
+ { jump over except handlers }
|
|
|
+ cg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
|
|
|
+
|
|
|
+ if fc_exit in tryflowcontrol then
|
|
|
+ begin
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,exittrylabel);
|
|
|
+ emit_scope_end;
|
|
|
+ cg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel);
|
|
|
+ end;
|
|
|
+ if fc_break in tryflowcontrol then
|
|
|
+ begin
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,breaktrylabel);
|
|
|
+ emit_scope_end;
|
|
|
+ cg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel);
|
|
|
+ end;
|
|
|
+ if fc_continue in tryflowcontrol then
|
|
|
+ begin
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,continuetrylabel);
|
|
|
+ emit_scope_end;
|
|
|
+ cg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel);
|
|
|
+ end;
|
|
|
+
|
|
|
+ { target for catch-all handler }
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,exceptlabel);
|
|
|
+
|
|
|
+ { set control flow labels for the except block }
|
|
|
+ { and the on statements }
|
|
|
+ current_procinfo.CurrExitLabel:=exitexceptlabel;
|
|
|
+ if assigned(oldBreakLabel) then
|
|
|
+ begin
|
|
|
+ current_procinfo.CurrContinueLabel:=continueexceptlabel;
|
|
|
+ current_procinfo.CurrBreakLabel:=breakexceptlabel;
|
|
|
+ end;
|
|
|
+
|
|
|
+ flowcontrol:=[fc_inflowcontrol];
|
|
|
+ { on statements }
|
|
|
+ if assigned(right) then
|
|
|
+ begin
|
|
|
+ { emit filter table to a temporary asmlist }
|
|
|
+ hlist:=TAsmList.Create;
|
|
|
+ new_section(hlist,sec_rodata,filterlabel.name,4);
|
|
|
+ cg.a_label(hlist,filterlabel);
|
|
|
+ onnodecount:=tai_const.create_32bit(0);
|
|
|
+ hlist.concat(onnodecount);
|
|
|
+
|
|
|
+ hnode:=right;
|
|
|
+ while assigned(hnode) do
|
|
|
+ begin
|
|
|
+ if hnode.nodetype<>onn then
|
|
|
+ InternalError(2011103101);
|
|
|
+ { TODO: make it done without using global label }
|
|
|
+ current_asmdata.getglobaljumplabel(onlabel);
|
|
|
+ hlist.concat(tai_const.create_sym(current_asmdata.RefAsmSymbol(tonnode(hnode).excepttype.vmt_mangledname,AT_DATA)));
|
|
|
+ hlist.concat(tai_const.create_sym(onlabel));
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,onlabel);
|
|
|
+ secondpass(hnode);
|
|
|
+ inc(onnodecount.value);
|
|
|
+ hnode:=tonnode(hnode).left;
|
|
|
+ end;
|
|
|
+ { add 'else' node to the filter list, too }
|
|
|
+ if assigned(t1) then
|
|
|
+ begin
|
|
|
+ hlist.concat(tai_const.create_32bit(-1));
|
|
|
+ hlist.concat(tai_const.create_sym(lastonlabel));
|
|
|
+ inc(onnodecount.value);
|
|
|
+ end;
|
|
|
+ { now move filter table to permanent list all at once }
|
|
|
+ maybe_new_object_file(current_asmdata.asmlists[al_typedconsts]);
|
|
|
+ current_asmdata.asmlists[al_typedconsts].concatlist(hlist);
|
|
|
+ hlist.free;
|
|
|
+ end;
|
|
|
+
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,lastonlabel);
|
|
|
+ if assigned(t1) then
|
|
|
+ begin
|
|
|
+ { here we don't have to reset flowcontrol }
|
|
|
+ { the default and on flowcontrols are handled equal }
|
|
|
+ secondpass(t1);
|
|
|
+ cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION');
|
|
|
+ if (flowcontrol*[fc_exit,fc_break,fc_continue]<>[]) then
|
|
|
+ cg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
|
|
|
+ end;
|
|
|
+ exceptflowcontrol:=flowcontrol;
|
|
|
+
|
|
|
+ if fc_exit in exceptflowcontrol then
|
|
|
+ begin
|
|
|
+ { do some magic for exit in the try block }
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,exitexceptlabel);
|
|
|
+ cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION');
|
|
|
+ cg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel);
|
|
|
+ end;
|
|
|
+
|
|
|
+ if fc_break in exceptflowcontrol then
|
|
|
+ begin
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,breakexceptlabel);
|
|
|
+ cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION');
|
|
|
+ cg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel);
|
|
|
+ end;
|
|
|
+
|
|
|
+ if fc_continue in exceptflowcontrol then
|
|
|
+ begin
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,continueexceptlabel);
|
|
|
+ cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION');
|
|
|
+ cg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel);
|
|
|
+ end;
|
|
|
+
|
|
|
+ cg.a_label(current_asmdata.CurrAsmList,endexceptlabel);
|
|
|
+
|
|
|
+errorexit:
|
|
|
+ { restore all saved labels }
|
|
|
+ endexceptlabel:=oldendexceptlabel;
|
|
|
+
|
|
|
+ { restore the control flow labels }
|
|
|
+ current_procinfo.CurrExitLabel:=oldCurrExitLabel;
|
|
|
+ if assigned(oldBreakLabel) then
|
|
|
+ begin
|
|
|
+ current_procinfo.CurrContinueLabel:=oldContinueLabel;
|
|
|
+ current_procinfo.CurrBreakLabel:=oldBreakLabel;
|
|
|
+ end;
|
|
|
+
|
|
|
+ { return all used control flow statements }
|
|
|
+ flowcontrol:=oldflowcontrol+(exceptflowcontrol +
|
|
|
+ tryflowcontrol - [fc_inflowcontrol]);
|
|
|
+ end;
|
|
|
+
|
|
|
+initialization
|
|
|
+ craisenode:=ti386raisenode;
|
|
|
+ connode:=ti386onnode;
|
|
|
+ ctryexceptnode:=ti386tryexceptnode;
|
|
|
+ ctryfinallynode:=ti386tryfinallynode;
|
|
|
+end.
|