From 2b59000d563d6177edf02ad3d6bdc73afbbeafba Mon Sep 17 00:00:00 2001 From: svenbarth Date: Tue, 21 Apr 2020 06:06:05 +0000 Subject: [PATCH] + implement compiler support for SEH on Win64 Note: due to the way we access variables in a nested function (which in this case includes exception filters) we can not extract the finally handlers and call them (like we do on i386 and x86_64, but instead we duplicate the finally code) git-svn-id: trunk@44941 - --- .gitattributes | 1 + compiler/aarch64/cgcpu.pas | 325 ++++++++++++++------ compiler/aarch64/cpunode.pas | 2 +- compiler/aarch64/cpupi.pas | 83 +++++- compiler/aarch64/ncpuflw.pas | 543 ++++++++++++++++++++++++++++++++++ compiler/aarch64/racpugas.pas | 227 +++++++++++++- compiler/aasmtai.pas | 22 +- compiler/ogcoff.pas | 7 +- compiler/psub.pas | 7 +- compiler/x86_64/win64unw.pas | 2 + 10 files changed, 1122 insertions(+), 97 deletions(-) create mode 100644 compiler/aarch64/ncpuflw.pas diff --git a/.gitattributes b/.gitattributes index 3f8af57f0a..5d00c8584c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -30,6 +30,7 @@ compiler/aarch64/itcpugas.pas svneol=native#text/plain compiler/aarch64/ncpuadd.pas svneol=native#text/plain compiler/aarch64/ncpucnv.pas svneol=native#text/plain compiler/aarch64/ncpucon.pas svneol=native#text/pascal +compiler/aarch64/ncpuflw.pas svneol=native#text/pascal compiler/aarch64/ncpuinl.pas svneol=native#text/plain compiler/aarch64/ncpumat.pas svneol=native#text/plain compiler/aarch64/ncpumem.pas svneol=native#text/plain diff --git a/compiler/aarch64/cgcpu.pas b/compiler/aarch64/cgcpu.pas index 5928ebdeae..b643a0eb82 100644 --- a/compiler/aarch64/cgcpu.pas +++ b/compiler/aarch64/cgcpu.pas @@ -124,10 +124,11 @@ interface implementation uses - globals,verbose,systems,cutils, + globals,verbose,systems,cutils,cclasses, paramgr,fmodule, symtable,symsym, tgobj, + ncgutil, procinfo,cpupi; @@ -1596,27 +1597,74 @@ implementation ref: treference; sr: tsuperregister; pairreg: tregister; + sehreg,sehregp : TAsmSehDirective; begin result:=0; reference_reset_base(ref,NR_SP,-16,ctempposinvalid,16,[]); ref.addressmode:=AM_PREINDEXED; pairreg:=NR_NO; - { store all used registers pairwise } - for sr:=lowsr to highsr do - if sr in rg[rt].used_in_proc then - if pairreg=NR_NO then - pairreg:=newreg(rt,sr,sub) + { for SEH on Win64 we can only store consecutive register pairs, others + need to be stored with STR } + if target_info.system=system_aarch64_win64 then + begin + if rt=R_INTREGISTER then + begin + sehreg:=ash_savereg_x; + sehregp:=ash_saveregp_x; + end + else if rt=R_MMREGISTER then + begin + sehreg:=ash_savefreg_x; + sehregp:=ash_savefregp_x; + end else + internalerror(2020041304); + for sr:=lowsr to highsr do + if sr in rg[rt].used_in_proc then + if pairreg=NR_NO then + pairreg:=newreg(rt,sr,sub) + else + begin + inc(result,16); + if getsupreg(pairreg)=sr-1 then + begin + list.concat(taicpu.op_reg_reg_ref(A_STP,pairreg,newreg(rt,sr,sub),ref)); + list.concat(cai_seh_directive.create_reg_offset(sehregp,pairreg,16)); + pairreg:=NR_NO; + end + else + begin + list.concat(taicpu.op_reg_ref(A_STR,pairreg,ref)); + list.concat(cai_seh_directive.create_reg_offset(sehreg,pairreg,16)); + pairreg:=newreg(rt,sr,sub); + end; + end; + if pairreg<>NR_NO then begin inc(result,16); - list.concat(taicpu.op_reg_reg_ref(A_STP,pairreg,newreg(rt,sr,sub),ref)); - pairreg:=NR_NO + list.concat(taicpu.op_reg_ref(A_STR,pairreg,ref)); + list.concat(cai_seh_directive.create_reg_offset(sehreg,pairreg,16)); end; - { one left -> store twice (stack must be 16 bytes aligned) } - if pairreg<>NR_NO then + end + else begin - list.concat(taicpu.op_reg_reg_ref(A_STP,pairreg,pairreg,ref)); - inc(result,16); + { store all used registers pairwise } + for sr:=lowsr to highsr do + if sr in rg[rt].used_in_proc then + if pairreg=NR_NO then + pairreg:=newreg(rt,sr,sub) + else + begin + inc(result,16); + list.concat(taicpu.op_reg_reg_ref(A_STP,pairreg,newreg(rt,sr,sub),ref)); + pairreg:=NR_NO + end; + { one left -> store twice (stack must be 16 bytes aligned) } + if pairreg<>NR_NO then + begin + list.concat(taicpu.op_reg_reg_ref(A_STP,pairreg,pairreg,ref)); + inc(result,16); + end; end; end; @@ -1637,69 +1685,124 @@ implementation procedure tcgaarch64.g_proc_entry(list: TAsmList; localsize: longint; nostackframe: boolean); var + hitem: tlinkedlistitem; + seh_proc: tai_seh_directive; + templist: TAsmList; + suppress_endprologue: boolean; ref: treference; totalstackframesize: longint; begin - if nostackframe then - exit; - { stack pointer has to be aligned to 16 bytes at all times } - localsize:=align(localsize,16); + hitem:=list.last; + { pi_has_unwind_info may already be set at this point if there are + SEH directives in assembler body. In this case, .seh_endprologue + is expected to be one of those directives, and not generated here. } + suppress_endprologue:=(pi_has_unwind_info in current_procinfo.flags); - { save stack pointer and return address } - reference_reset_base(ref,NR_SP,-16,ctempposinvalid,16,[]); - ref.addressmode:=AM_PREINDEXED; - list.concat(taicpu.op_reg_reg_ref(A_STP,NR_FP,NR_LR,ref)); - { initialise frame pointer } - a_load_reg_reg(list,OS_ADDR,OS_ADDR,NR_SP,NR_FP); - - totalstackframesize:=localsize; - { save modified integer registers } - inc(totalstackframesize, - save_regs(list,R_INTREGISTER,RS_X19,RS_X28,R_SUBWHOLE)); - { only the lower 64 bits of the modified vector registers need to be - saved; if the caller needs the upper 64 bits, it has to save them - itself } - inc(totalstackframesize, - save_regs(list,R_MMREGISTER,RS_D8,RS_D15,R_SUBMMD)); - - { allocate stack space } - if localsize<>0 then + if not nostackframe then begin + { stack pointer has to be aligned to 16 bytes at all times } localsize:=align(localsize,16); - current_procinfo.final_localsize:=localsize; - handle_reg_imm12_reg(list,A_SUB,OS_ADDR,NR_SP,localsize,NR_SP,NR_IP0,false,true); - end; - { By default, we use the frame pointer to access parameters passed via - the stack and the stack pointer to address local variables and temps - because - a) we can use bigger positive than negative offsets (so accessing - locals via negative offsets from the frame pointer would be less - efficient) - b) we don't know the local size while generating the code, so - accessing the parameters via the stack pointer is not possible - without copying them - The problem with this is the get_frame() intrinsic: - a) it must return the same value as what we pass as parentfp - parameter, since that's how it's used in the TP-style objects unit - b) its return value must usable to access all local data from a - routine (locals and parameters), since it's all the nested - routines have access to - c) its return value must be usable to construct a backtrace, as it's - also used by the exception handling routines - The solution we use here, based on something similar that's done in - the MIPS port, is to generate all accesses to locals in the routine - itself SP-relative, and then after the code is generated and the local - size is known (namely, here), we change all SP-relative variables/ - parameters into FP-relative ones. This means that they'll be accessed - less efficiently from nested routines, but those accesses are indirect - anyway and at least this way they can be accessed at all - } - if current_procinfo.has_nestedprocs then - begin - current_procinfo.procdef.localst.SymList.ForEachCall(@FixupOffsets,@totalstackframesize); - current_procinfo.procdef.parast.SymList.ForEachCall(@FixupOffsets,@totalstackframesize); + if target_info.system=system_aarch64_win64 then + include(current_procinfo.flags,pi_has_unwind_info); + + { save stack pointer and return address } + reference_reset_base(ref,NR_SP,-16,ctempposinvalid,16,[]); + ref.addressmode:=AM_PREINDEXED; + list.concat(taicpu.op_reg_reg_ref(A_STP,NR_FP,NR_LR,ref)); + if target_info.system=system_aarch64_win64 then + list.concat(cai_seh_directive.create_offset(ash_savefplr_x,16)); + { initialise frame pointer } + if current_procinfo.procdef.proctypeoption<>potype_exceptfilter then + begin + a_load_reg_reg(list,OS_ADDR,OS_ADDR,NR_SP,NR_FP); + if target_info.system=system_aarch64_win64 then + list.concat(cai_seh_directive.create(ash_setfp)); + end + else + begin + gen_load_frame_for_exceptfilter(list); + localsize:=current_procinfo.maxpushedparasize; + end; + + totalstackframesize:=localsize; + { save modified integer registers } + inc(totalstackframesize, + save_regs(list,R_INTREGISTER,RS_X19,RS_X28,R_SUBWHOLE)); + { only the lower 64 bits of the modified vector registers need to be + saved; if the caller needs the upper 64 bits, it has to save them + itself } + inc(totalstackframesize, + save_regs(list,R_MMREGISTER,RS_D8,RS_D15,R_SUBMMD)); + + { allocate stack space } + if localsize<>0 then + begin + localsize:=align(localsize,16); + current_procinfo.final_localsize:=localsize; + handle_reg_imm12_reg(list,A_SUB,OS_ADDR,NR_SP,localsize,NR_SP,NR_IP0,false,true); + if target_info.system=system_aarch64_win64 then + list.concat(cai_seh_directive.create_offset(ash_stackalloc,localsize)); + end; + { By default, we use the frame pointer to access parameters passed via + the stack and the stack pointer to address local variables and temps + because + a) we can use bigger positive than negative offsets (so accessing + locals via negative offsets from the frame pointer would be less + efficient) + b) we don't know the local size while generating the code, so + accessing the parameters via the stack pointer is not possible + without copying them + The problem with this is the get_frame() intrinsic: + a) it must return the same value as what we pass as parentfp + parameter, since that's how it's used in the TP-style objects unit + b) its return value must usable to access all local data from a + routine (locals and parameters), since it's all the nested + routines have access to + c) its return value must be usable to construct a backtrace, as it's + also used by the exception handling routines + + The solution we use here, based on something similar that's done in + the MIPS port, is to generate all accesses to locals in the routine + itself SP-relative, and then after the code is generated and the local + size is known (namely, here), we change all SP-relative variables/ + parameters into FP-relative ones. This means that they'll be accessed + less efficiently from nested routines, but those accesses are indirect + anyway and at least this way they can be accessed at all + } + if current_procinfo.has_nestedprocs or + ( + (target_info.system=system_aarch64_win64) and + (current_procinfo.flags*[pi_has_implicit_finally,pi_needs_implicit_finally,pi_uses_exceptions]<>[]) + ) then + begin + current_procinfo.procdef.localst.SymList.ForEachCall(@FixupOffsets,@totalstackframesize); + current_procinfo.procdef.parast.SymList.ForEachCall(@FixupOffsets,@totalstackframesize); + end; end; + + if not (pi_has_unwind_info in current_procinfo.flags) then + exit; + + { Generate unwind data for aarch64-win64 } + seh_proc:=cai_seh_directive.create_name(ash_proc,current_procinfo.procdef.mangledname); + if assigned(hitem) then + list.insertafter(seh_proc,hitem) + else + list.insert(seh_proc); + { the directive creates another section } + inc(list.section_count); + templist:=TAsmList.Create; + + if not suppress_endprologue then + begin + templist.concat(cai_seh_directive.create(ash_endprologue)); + end; + if assigned(current_procinfo.endprologue_ai) then + current_procinfo.aktproccode.insertlistafter(current_procinfo.endprologue_ai,templist) + else + list.concatlist(templist); + templist.free; end; @@ -1720,35 +1823,76 @@ implementation ref: treference; sr, highestsetsr: tsuperregister; pairreg: tregister; + i, regcount: longint; + aiarr : array of tai; begin reference_reset_base(ref,NR_SP,16,ctempposinvalid,16,[]); ref.addressmode:=AM_POSTINDEXED; - { highest reg stored twice? } regcount:=0; - highestsetsr:=RS_NO; - for sr:=lowsr to highsr do - if sr in rg[rt].used_in_proc then - begin - inc(regcount); - highestsetsr:=sr; - end; - if odd(regcount) then + { due to SEH on Win64 we can only load consecutive registers and single + ones are done using LDR, so we need to handle this differently there } + if target_info.system=system_aarch64_win64 then begin - list.concat(taicpu.op_reg_ref(A_LDR,newreg(rt,highestsetsr,sub),ref)); - highestsetsr:=pred(highestsetsr); - end; - { load all (other) used registers pairwise } - pairreg:=NR_NO; - for sr:=highestsetsr downto lowsr do - if sr in rg[rt].used_in_proc then - if pairreg=NR_NO then - pairreg:=newreg(rt,sr,sub) - else + setlength(aiarr,highsr-lowsr+1); + pairreg:=NR_NO; + for sr:=lowsr to highsr do + if sr in rg[rt].used_in_proc then + begin + if pairreg=NR_NO then + pairreg:=newreg(rt,sr,sub) + else + begin + if getsupreg(pairreg)=sr-1 then + begin + aiarr[regcount]:=taicpu.op_reg_reg_ref(A_LDP,pairreg,newreg(rt,sr,sub),ref); + inc(regcount); + pairreg:=NR_NO; + end + else + begin + aiarr[regcount]:=taicpu.op_reg_ref(A_LDR,pairreg,ref); + inc(regcount); + pairreg:=newreg(rt,sr,sub); + end; + end; + end; + if pairreg<>NR_NO then begin - list.concat(taicpu.op_reg_reg_ref(A_LDP,newreg(rt,sr,sub),pairreg,ref)); - pairreg:=NR_NO + aiarr[regcount]:=taicpu.op_reg_ref(A_LDR,pairreg,ref); + inc(regcount); + pairreg:=NR_NO; end; + for i:=regcount-1 downto 0 do + list.concat(aiarr[i]); + end + else + begin + { highest reg stored twice? } + highestsetsr:=RS_NO; + for sr:=lowsr to highsr do + if sr in rg[rt].used_in_proc then + begin + inc(regcount); + highestsetsr:=sr; + end; + if odd(regcount) then + begin + list.concat(taicpu.op_reg_ref(A_LDR,newreg(rt,highestsetsr,sub),ref)); + highestsetsr:=pred(highestsetsr); + end; + { load all (other) used registers pairwise } + pairreg:=NR_NO; + for sr:=highestsetsr downto lowsr do + if sr in rg[rt].used_in_proc then + if pairreg=NR_NO then + pairreg:=newreg(rt,sr,sub) + else + begin + list.concat(taicpu.op_reg_reg_ref(A_LDP,newreg(rt,sr,sub),pairreg,ref)); + pairreg:=NR_NO + end; + end; { There can't be any register left } if pairreg<>NR_NO then internalerror(2014112602); @@ -1807,6 +1951,11 @@ implementation { return } list.concat(taicpu.op_none(A_RET)); + if (pi_has_unwind_info in current_procinfo.flags) then + begin + tcpuprocinfo(current_procinfo).dump_scopes(list); + list.concat(cai_seh_directive.create(ash_endproc)); + end; end; diff --git a/compiler/aarch64/cpunode.pas b/compiler/aarch64/cpunode.pas index 1e6c51ef2b..b59f3e02e8 100644 --- a/compiler/aarch64/cpunode.pas +++ b/compiler/aarch64/cpunode.pas @@ -35,7 +35,7 @@ implementation symcpu, aasmdef, {$ifndef llvm} - ncpuadd,ncpumat,ncpumem,ncpuinl,ncpucnv,ncpuset,ncpucon + ncpuadd,ncpumat,ncpumem,ncpuinl,ncpucnv,ncpuset,ncpucon,ncpuflw {$else llvm} llvmnode {$endif llvm} diff --git a/compiler/aarch64/cpupi.pas b/compiler/aarch64/cpupi.pas index 86b06e5c45..617853090c 100644 --- a/compiler/aarch64/cpupi.pas +++ b/compiler/aarch64/cpupi.pas @@ -27,19 +27,38 @@ interface uses procinfo, - psub; + psub, + aasmdata,aasmbase; type tcpuprocinfo=class(tcgprocinfo) + private + scopes: TAsmList; + scopecount: longint; + unwindflags: byte; + public constructor create(aparent: tprocinfo); override; + destructor destroy; override; procedure set_first_temp_offset; override; + procedure add_finally_scope(startlabel,endlabel,handler:TAsmSymbol;implicit:Boolean); + procedure add_except_scope(trylabel,exceptlabel,endlabel,filter:TAsmSymbol); + procedure dump_scopes(list:tasmlist); end; implementation uses + cutils, + fmodule, + symtable, tgobj, - cpubase; + cpubase, + aasmtai; + + const + SCOPE_FINALLY=0; + SCOPE_CATCHALL=1; + SCOPE_IMPLICIT=2; constructor tcpuprocinfo.create(aparent: tprocinfo); begin @@ -56,12 +75,72 @@ implementation framepointer:=NR_STACK_POINTER_REG; end; + destructor tcpuprocinfo.destroy; + begin + scopes.free; + inherited destroy; + end; + procedure tcpuprocinfo.set_first_temp_offset; begin { leave room for allocated parameters } tg.setfirsttemp(align(maxpushedparasize,16)); end; + procedure tcpuprocinfo.add_finally_scope(startlabel,endlabel,handler:TAsmSymbol;implicit:Boolean); + begin + unwindflags:=unwindflags or 2; + if implicit then { also needs catch functionality } + unwindflags:=unwindflags or 1; + inc(scopecount); + if scopes=nil then + scopes:=TAsmList.Create; + + if implicit then + scopes.concat(tai_const.create_32bit(SCOPE_IMPLICIT)) + else + scopes.concat(tai_const.create_32bit(SCOPE_FINALLY)); + scopes.concat(tai_const.create_rva_sym(startlabel)); + scopes.concat(tai_const.create_rva_sym(endlabel)); + scopes.concat(tai_const.create_rva_sym(handler)); + end; + + procedure tcpuprocinfo.add_except_scope(trylabel,exceptlabel,endlabel,filter:TAsmSymbol); + begin + unwindflags:=unwindflags or 3; + inc(scopecount); + if scopes=nil then + scopes:=TAsmList.Create; + + if Assigned(filter) then + scopes.concat(tai_const.create_rva_sym(filter)) + else + scopes.concat(tai_const.create_32bit(SCOPE_CATCHALL)); + scopes.concat(tai_const.create_rva_sym(trylabel)); + scopes.concat(tai_const.create_rva_sym(exceptlabel)); + scopes.concat(tai_const.create_rva_sym(endlabel)); + end; + + procedure tcpuprocinfo.dump_scopes(list: tasmlist); + var + hdir: tai_seh_directive; + begin + if (scopecount=0) then + exit; + hdir:=cai_seh_directive.create_name(ash_handler,'__FPC_specific_handler'); + if not systemunit.iscurrentunit then + current_module.add_extern_asmsym('__FPC_specific_handler',AB_EXTERNAL,AT_FUNCTION); + hdir.data.flags:=unwindflags; + list.concat(hdir); + list.concat(cai_seh_directive.create(ash_handlerdata)); + inc(list.section_count); + list.concat(tai_const.create_32bit(scopecount)); + list.concatlist(scopes); + { return to text, required for GAS compatibility } + { This creates a tai_align which is redundant here (although harmless) } + new_section(list,sec_code,lower(procdef.mangledname),0); + end; + begin cprocinfo:=tcpuprocinfo; diff --git a/compiler/aarch64/ncpuflw.pas b/compiler/aarch64/ncpuflw.pas new file mode 100644 index 0000000000..c5faff991c --- /dev/null +++ b/compiler/aarch64/ncpuflw.pas @@ -0,0 +1,543 @@ +{ + Copyright (c) 2011-2020 by Free Pascal development team + + Generate Win64-specific exception handling code (based on x86_64 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 ncpuflw; + +{$i fpcdefs.inc} + +interface + + uses + node,nflw,ncgflw,psub; + + type + taarch64raisenode=class(tcgraisenode) + function pass_1 : tnode;override; + end; + + taarch64onnode=class(tcgonnode) + procedure pass_generate_code;override; + end; + + taarch64tryexceptnode=class(tcgtryexceptnode) + procedure pass_generate_code;override; + end; + + taarch64tryfinallynode=class(tcgtryfinallynode) + finalizepi: tcgprocinfo; + constructor create(l,r:TNode);override; + constructor create_implicit(l,r:TNode);override; + function simplify(forinline: boolean): tnode;override; + procedure pass_generate_code;override; + end; + +implementation + + uses + globtype,globals,verbose,systems,fmodule, + nbas,ncal,nutils, + symconst,symsym,symdef, + cgbase,cgobj,cgutils,tgobj, + cpubase,htypechk, + pass_1,pass_2, + aasmbase,aasmtai,aasmdata,aasmcpu,procinfo,cpupi; + + var + endexceptlabel: tasmlabel; + + +{ taarch64raisenode } + +function taarch64raisenode.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_aarch64_win64) 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; + +{ taarch64onnode } + +procedure taarch64onnode.pass_generate_code; + var + exceptvarsym : tlocalvarsym; + begin + if (target_info.system<>system_aarch64_win64) then + begin + inherited pass_generate_code; + exit; + end; + + location_reset(location,LOC_VOID,OS_NO); + + { RTL will put exceptobject into X0 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); + end; + +{ taarch64tryfinallynode } + +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); + else + ; + 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); + else + ; + end; + result:=fen_true; + end; + +constructor taarch64tryfinallynode.create(l, r: TNode); + begin + inherited create(l,r); + if (target_info.system=system_aarch64_win64) and + { 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. } + not (df_generic in current_procinfo.procdef.defoptions) then + begin + finalizepi:=tcgprocinfo(current_procinfo.create_for_outlining('$fin$',current_procinfo.procdef.struct,potype_exceptfilter,voidtype,r)); + { the init/final code is messing with asm nodes, so inform the compiler about this } + include(finalizepi.flags,pi_has_assembler_block); + { 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); + end; + end; + +constructor taarch64tryfinallynode.create_implicit(l, r: TNode); + begin + inherited create_implicit(l, r); + if (target_info.system=system_aarch64_win64) then + begin + if df_generic in current_procinfo.procdef.defoptions then + InternalError(2020033101); + + finalizepi:=tcgprocinfo(current_procinfo.create_for_outlining('$fin$',current_procinfo.procdef.struct,potype_exceptfilter,voidtype,r)); + include(finalizepi.flags,pi_do_call); + { the init/final code is messing with asm nodes, so inform the compiler about this } + include(finalizepi.flags,pi_has_assembler_block); + finalizepi.allocate_push_parasize(32); + end; + end; + +function taarch64tryfinallynode.simplify(forinline: boolean): tnode; + begin + result:=inherited simplify(forinline); + if (target_info.system<>system_aarch64_win64) then + exit; + if (result=nil) then + begin + { generate a copy of the code } + finalizepi.code:=right.getcopy; + foreachnodestatic(right,@copy_parasize,finalizepi); + { 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; + end; + end; + end; + +procedure emit_nop; + var + dummy: TAsmLabel; + begin + { To avoid optimizing away the whole thing, prepend a jumplabel with increased refcount } + current_asmdata.getjumplabel(dummy); + dummy.increfs; + cg.a_label(current_asmdata.CurrAsmList,dummy); + current_asmdata.CurrAsmList.concat(Taicpu.op_none(A_NOP)); + end; + +procedure taarch64tryfinallynode.pass_generate_code; + var + trylabel, + endtrylabel, + finallylabel, + endfinallylabel, + templabel, + oldexitlabel: tasmlabel; + oldflowcontrol: tflowcontrol; + catch_frame: boolean; + begin + if (target_info.system<>system_aarch64_win64) then + begin + inherited pass_generate_code; + exit; + end; + + location_reset(location,LOC_VOID,OS_NO); + + { Do not generate a frame that catches exceptions if the only action + would be reraising it. Doing so is extremely inefficient with SEH + (in contrast with setjmp/longjmp exception handling) } + catch_frame:=implicitframe and + (current_procinfo.procdef.proccalloption=pocall_safecall); + + oldflowcontrol:=flowcontrol; + flowcontrol:=[fc_inflowcontrol]; + + templabel:=nil; + current_asmdata.getjumplabel(trylabel); + current_asmdata.getjumplabel(endtrylabel); + current_asmdata.getjumplabel(finallylabel); + current_asmdata.getjumplabel(endfinallylabel); + oldexitlabel:=current_procinfo.CurrExitLabel; + if implicitframe then + current_procinfo.CurrExitLabel:=finallylabel; + + { Start of scope } + { Padding with NOP is necessary here because exceptions in called + procedures are seen at the next instruction, while CPU/OS exceptions + like AV are seen at the current instruction. + + So in the following code + + raise_some_exception; //(a) + try + pchar(nil)^:='0'; //(b) + ... + + without NOP, exceptions (a) and (b) will be seen at the same address + and fall into the same scope. However they should be seen in different scopes. + } + + emit_nop; + cg.a_label(current_asmdata.CurrAsmList,trylabel); + + { try code } + if assigned(left) then + begin + { fc_unwind_xx tells exit/continue/break statements to emit special + unwind code instead of just JMP } + if not implicitframe then + flowcontrol:=flowcontrol+[fc_catching_exceptions,fc_unwind_exit,fc_unwind_loop]; + secondpass(left); + flowcontrol:=flowcontrol-[fc_catching_exceptions,fc_unwind_exit,fc_unwind_loop]; + if codegenerror then + exit; + end; + + { finallylabel is only used in implicit frames as an exit point from nested try..finally + statements, if any. To prevent finalizer from being executed twice, it must come before + endtrylabel (bug #34772) } + if catch_frame then + begin + current_asmdata.getjumplabel(templabel); + cg.a_label(current_asmdata.CurrAsmList, finallylabel); + { jump over exception handler } + cg.a_jmp_always(current_asmdata.CurrAsmList,templabel); + { Handle the except block first, so endtrylabel serves both + as end of scope and as unwind target. This way it is possible to + encode everything into a single scope record. } + cg.a_label(current_asmdata.CurrAsmList,endtrylabel); + if (current_procinfo.procdef.proccalloption=pocall_safecall) then + begin + handle_safecall_exception; + cg.a_jmp_always(current_asmdata.CurrAsmList,endfinallylabel); + end + else + InternalError(2014031601); + cg.a_label(current_asmdata.CurrAsmList,templabel); + end + else + begin + { same as emit_nop but using finallylabel instead of dummy } + cg.a_label(current_asmdata.CurrAsmList,finallylabel); + finallylabel.increfs; + current_asmdata.CurrAsmList.concat(Taicpu.op_none(A_NOP)); + cg.a_label(current_asmdata.CurrAsmList,endtrylabel); + end; + + flowcontrol:=[fc_inflowcontrol]; + { store the tempflags so that we can generate a copy of the finally handler + later on } + if not implicitframe then + finalizepi.store_tempflags; + { generate the inline finalizer code } + secondpass(right); + + if codegenerror then + exit; + + { normal exit from safecall proc must zero the result register } + if implicitframe and (current_procinfo.procdef.proccalloption=pocall_safecall) then + cg.a_load_const_reg(current_asmdata.CurrAsmList,OS_INT,0,NR_FUNCTION_RESULT_REG); + + cg.a_label(current_asmdata.CurrAsmList,endfinallylabel); + + { generate the scope record in .xdata } + tcpuprocinfo(current_procinfo).add_finally_scope(trylabel,endtrylabel, + current_asmdata.RefAsmSymbol(finalizepi.procdef.mangledname,AT_FUNCTION),catch_frame); + + if implicitframe then + current_procinfo.CurrExitLabel:=oldexitlabel; + flowcontrol:=oldflowcontrol; + end; + +{ taarch64tryexceptnode } + +procedure taarch64tryexceptnode.pass_generate_code; + var + trylabel, + exceptlabel,oldendexceptlabel, + lastonlabel, + exitexceptlabel, + continueexceptlabel, + breakexceptlabel, + oldCurrExitLabel, + oldContinueLabel, + oldBreakLabel : tasmlabel; + onlabel, + filterlabel: tasmlabel; + oldflowcontrol,tryflowcontrol, + exceptflowcontrol : tflowcontrol; + hnode : tnode; + hlist : tasmlist; + onnodecount : tai_const; + sym : tasmsymbol; + label + errorexit; + begin + if (target_info.system<>system_aarch64_win64) then + begin + inherited pass_generate_code; + exit; + end; + location_reset(location,LOC_VOID,OS_NO); + + oldflowcontrol:=flowcontrol; + exceptflowcontrol:=[]; + continueexceptlabel:=nil; + breakexceptlabel:=nil; + + include(flowcontrol,fc_inflowcontrol); + { this can be called recursivly } + oldBreakLabel:=nil; + oldContinueLabel:=nil; + oldendexceptlabel:=endexceptlabel; + + { save the old labels for control flow statements } + oldCurrExitLabel:=current_procinfo.CurrExitLabel; + current_asmdata.getjumplabel(exitexceptlabel); + if assigned(current_procinfo.CurrBreakLabel) then + begin + oldContinueLabel:=current_procinfo.CurrContinueLabel; + oldBreakLabel:=current_procinfo.CurrBreakLabel; + 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 } + current_asmdata.getjumplabel(trylabel); + emit_nop; + cg.a_label(current_asmdata.CurrAsmList,trylabel); + + { control flow in try block needs no special handling, + just make sure that target labels are outside the scope } + secondpass(left); + tryflowcontrol:=flowcontrol; + if codegenerror then + goto errorexit; + + { jump over except handlers } + cg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel); + + { end of scope } + 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; + current_asmdata.getaddrlabel(filterlabel); + new_section(hlist,sec_rodata_norel,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); + current_asmdata.getjumplabel(onlabel); + sym:=current_asmdata.RefAsmSymbol(tonnode(hnode).excepttype.vmt_mangledname,AT_DATA,true); + hlist.concat(tai_const.create_rva_sym(sym)); + hlist.concat(tai_const.create_rva_sym(onlabel)); + current_module.add_extern_asmsym(sym); + 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_rva_sym(lastonlabel)); + inc(onnodecount.value); + end; + { now move filter table to permanent list all at once } + current_procinfo.aktlocaldata.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'); + if (fc_unwind_exit in oldflowcontrol) then + cg.g_local_unwind(current_asmdata.CurrAsmList,oldCurrExitLabel) + else + 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'); + if (fc_unwind_loop in oldflowcontrol) then + cg.g_local_unwind(current_asmdata.CurrAsmList,oldBreakLabel) + else + 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'); + if (fc_unwind_loop in oldflowcontrol) then + cg.g_local_unwind(current_asmdata.CurrAsmList,oldContinueLabel) + else + cg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel); + end; + + emit_nop; + cg.a_label(current_asmdata.CurrAsmList,endexceptlabel); + tcpuprocinfo(current_procinfo).add_except_scope(trylabel,exceptlabel,endexceptlabel,filterlabel); + +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:=taarch64raisenode; + connode:=taarch64onnode; + ctryexceptnode:=taarch64tryexceptnode; + ctryfinallynode:=taarch64tryfinallynode; +end. + diff --git a/compiler/aarch64/racpugas.pas b/compiler/aarch64/racpugas.pas index 0c9b870dbb..622ad127a5 100644 --- a/compiler/aarch64/racpugas.pas +++ b/compiler/aarch64/racpugas.pas @@ -28,14 +28,21 @@ Unit racpugas; uses raatt,racpu, + aasmtai, cpubase; type + + { taarch64attreader } + taarch64attreader = class(tattreader) actoppostfix : TOpPostfix; + actsehdirective : TAsmSehDirective; function is_asmopcode(const s: string):boolean;override; function is_register(const s:string):boolean;override; + function is_targetdirective(const s: string): boolean;override; procedure handleopcode;override; + procedure handletargetdirective; override; procedure BuildReference(oper: taarch64operand; is64bit: boolean); procedure BuildOperand(oper: taarch64operand; is64bit: boolean); function TryBuildShifterOp(instr: taarch64instruction; opnr: longint) : boolean; @@ -53,7 +60,7 @@ Unit racpugas; cutils, { global } globtype,verbose, - systems,aasmbase,aasmtai,aasmdata,aasmcpu, + systems,aasmbase,aasmdata,aasmcpu, { symtable } symconst,symsym,symdef, procinfo, @@ -98,6 +105,46 @@ Unit racpugas; end; + const + { Aarch64 subset of SEH directives. .seh_proc, .seh_endproc and .seh_endepilogue + excluded because they are generated automatically when needed. } + recognized_directives: set of TAsmSehDirective=[ + ash_endprologue,ash_handler,ash_handlerdata, + ash_stackalloc,ash_nop,ash_savefplr,ash_savefplr_x, + ash_savereg,ash_savereg_x,ash_saveregp,ash_saveregp_x, + ash_savefreg,ash_savefreg_x,ash_savefregp,ash_savefregp_x, + ash_setfp,ash_addfp + ]; + + + function taarch64attreader.is_targetdirective(const s: string): boolean; + var + i: TAsmSehDirective; + begin + result:=false; + if target_info.system<>system_aarch64_win64 then + exit; + + for i:=low(TAsmSehDirective) to high(TAsmSehDirective) do + begin + if not (i in recognized_directives) then + continue; + if s=sehdirectivestr[i] then + begin + actsehdirective:=i; + result:=true; + break; + end; + end; + { allow SEH directives only in pure assember routines } + if result and not (po_assembler in current_procinfo.procdef.procoptions) then + begin + Message(asmr_e_seh_in_pure_asm_only); + result:=false; + end; + end; + + procedure taarch64attreader.ReadSym(oper: taarch64operand; is64bit: boolean); var tempstr, mangledname : string; @@ -1035,6 +1082,184 @@ Unit racpugas; end; + procedure taarch64attreader.handletargetdirective; + + function maxoffset(ash:TAsmSehDirective):aint; + begin + case ash of + ash_savefplr, + ash_saveregp, + ash_savereg, + ash_savefregp, + ash_savefreg: + result:=504; + ash_savefplr_x, + ash_saveregp_x, + ash_savefregp_x: + result:=-512; + ash_savereg_x, + ash_savefreg_x: + result:=-256; + ash_addfp: + result:=2040; + else + internalerror(2020041204); + end; + end; + + procedure add_reg_with_offset(ash:TAsmSehDirective;hreg:tregister;hnum:aint;neg:boolean); + begin + if (neg and ((hnum>0) or (hnum0))) or + (not neg and ((hnum<0) or (hnum>maxoffset(ash)) or ((hnum and $7)<>0))) then + Message1(asmr_e_bad_seh_directive_offset,sehdirectivestr[actsehdirective]) + else + begin + if neg then + hnum:=-hnum; + if hreg=NR_NO then + curlist.concat(cai_seh_directive.create_offset(actsehdirective,hnum)) + else + curlist.concat(cai_seh_directive.create_reg_offset(actsehdirective,hreg,hnum)); + end; + end; + + var + hreg, + hreg2 : TRegister; + hnum : aint; + flags : integer; + ai : tai_seh_directive; + hs : string; + err : boolean; + begin + if actasmtoken<>AS_TARGET_DIRECTIVE then + InternalError(2020033102); + Consume(AS_TARGET_DIRECTIVE); + Include(current_procinfo.flags,pi_has_unwind_info); + + case actsehdirective of + ash_nop, + ash_setfp, + ash_endprologue, + ash_handlerdata: + curlist.concat(cai_seh_directive.create(actsehdirective)); + + ash_handler: + begin + hs:=actasmpattern; + Consume(AS_ID); + flags:=0; + err:=false; + while actasmtoken=AS_COMMA do + begin + Consume(AS_COMMA); + if actasmtoken=AS_AT then + begin + Consume(AS_AT); + if actasmtoken=AS_ID then + begin + uppervar(actasmpattern); + if actasmpattern='EXCEPT' then + flags:=flags or 1 + else if actasmpattern='UNWIND' then + flags:=flags or 2 + else + err:=true; + Consume(AS_ID); + end + else + err:=true; + end + else + err:=true; + if err then + begin + Message(asmr_e_syntax_error); + RecoverConsume(false); + exit; + end; + end; + + ai:=cai_seh_directive.create_name(ash_handler,hs); + ai.data.flags:=flags; + curlist.concat(ai); + end; + ash_savefplr, + ash_savefplr_x: + begin + hnum:=BuildConstExpression(false,false); + add_reg_with_offset(actsehdirective,NR_NO,hnum,actsehdirective=ash_savefplr_x); + end; + ash_savereg, + ash_savereg_x: + begin + hreg:=actasmregister; + Consume(AS_REGISTER); + if (getregtype(hreg)<>R_INTREGISTER) or (getsubreg(hreg)<>R_SUBWHOLE) or (getsupreg(hreg)<19) then + Message1(asmr_e_bad_seh_directive_register,sehdirectivestr[actsehdirective]); + Consume(AS_COMMA); + hnum:=BuildConstExpression(false,false); + add_reg_with_offset(actsehdirective,hreg,hnum,actsehdirective=ash_savereg_x); + end; + ash_saveregp, + ash_saveregp_x: + begin + hreg:=actasmregister; + consume(AS_REGISTER); + if (getregtype(hreg)<>R_INTREGISTER) or (getsubreg(hreg)<>R_SUBWHOLE) or (getsupreg(hreg)<19) then + Message1(asmr_e_bad_seh_directive_register,sehdirectivestr[actsehdirective]); + consume(AS_COMMA); + hreg2:=actasmregister; + consume(AS_REGISTER); + if (getregtype(hreg2)<>R_INTREGISTER) or (getsubreg(hreg2)<>R_SUBWHOLE) or (getsupreg(hreg2)<>getsupreg(hreg)+1) then + Message1(asmr_e_bad_seh_directive_register,sehdirectivestr[actsehdirective]); + consume(AS_COMMA); + hnum:=BuildConstExpression(false,false); + add_reg_with_offset(actsehdirective,hreg,hnum,actsehdirective=ash_saveregp_x); + end; + ash_savefreg, + ash_savefreg_x: + begin + hreg:=actasmregister; + Consume(AS_REGISTER); + if (getregtype(hreg)<>R_MMREGISTER) or (getsubreg(hreg)<>R_SUBWHOLE) or (getsupreg(hreg)<8) then + Message1(asmr_e_bad_seh_directive_register,sehdirectivestr[actsehdirective]); + Consume(AS_COMMA); + hnum:=BuildConstExpression(false,false); + add_reg_with_offset(actsehdirective,hreg,hnum,actsehdirective=ash_savefreg_x); + end; + ash_savefregp, + ash_savefregp_x: + begin + hreg:=actasmregister; + consume(AS_REGISTER); + if (getregtype(hreg)<>R_MMREGISTER) or (getsubreg(hreg)<>R_SUBWHOLE) or (getsupreg(hreg)<8) then + Message1(asmr_e_bad_seh_directive_register,sehdirectivestr[actsehdirective]); + consume(AS_COMMA); + hreg2:=actasmregister; + consume(AS_REGISTER); + if (getregtype(hreg2)<>R_MMREGISTER) or (getsubreg(hreg2)<>R_SUBWHOLE) or (getsupreg(hreg2)<>getsupreg(hreg)+1) then + Message1(asmr_e_bad_seh_directive_register,sehdirectivestr[actsehdirective]); + consume(AS_COMMA); + hnum:=BuildConstExpression(false,false); + add_reg_with_offset(actsehdirective,hreg,hnum,actsehdirective=ash_savefregp_x); + end; + ash_stackalloc: + begin + hnum:=BuildConstExpression(false,false); + if (hnum<0) or (hnum>$FFFFFF) or ((hnum and 7)<>0) then + Message1(asmr_e_bad_seh_directive_offset,sehdirectivestr[ash_stackalloc]) + else + curlist.concat(cai_seh_directive.create_offset(ash_stackalloc,hnum)); + end; + else + InternalError(2020033103); + end; + if actasmtoken<>AS_SEPARATOR then + Consume(AS_SEPARATOR); + end; + + {***************************************************************************** Initialize *****************************************************************************} diff --git a/compiler/aasmtai.pas b/compiler/aasmtai.pas index 2d2655cc96..f700e731c9 100644 --- a/compiler/aasmtai.pas +++ b/compiler/aasmtai.pas @@ -398,7 +398,10 @@ interface ash_endprologue,ash_handler,ash_handlerdata, ash_eh,ash_32,ash_no32, ash_setframe,ash_stackalloc,ash_pushreg, - ash_savereg,ash_savexmm,ash_pushframe, + ash_savereg,ash_savereg_x,ash_saveregp,ash_saveregp_x, + ash_savexmm,ash_savefreg,ash_savefreg_x,ash_savefregp,ash_savefregp_x,ash_pushframe, + ash_setfp,ash_addfp,ash_savefplr,ash_savefplr_x, + ash_nop, ash_pushnv,ash_savenv ); @@ -439,7 +442,10 @@ interface '.seh_endprologue','.seh_handler','.seh_handlerdata', '.seh_eh','.seh_32','seh_no32', '.seh_setframe','.seh_stackalloc','.seh_pushreg', - '.seh_savereg','.seh_savexmm','.seh_pushframe', + '.seh_savereg','.seh_savereg_x','.seh_saveregp','.seh_saveregp_x', + '.seh_savexmm','.seh_savefreg','.seh_savefreg_x','.seh_savefregp','.seh_savefregp_x','.seh_pushframe', + '.seh_setfp','.seh_addfp','.seh_savefplr','.seh_savefplr_x', + '.seh_nop', '.pushnv','.savenv' ); symbolpairkindstr: array[TSymbolPairKind] of string[11]=( @@ -3334,8 +3340,20 @@ implementation sd_offset, { stackalloc } sd_reg, { pushreg } sd_regoffset, { savereg } + sd_regoffset, { savereg_x } + sd_regoffset, { saveregp } + sd_regoffset, { saveregp_x } sd_regoffset, { savexmm } + sd_regoffset, { savefreg } + sd_regoffset, { savefreg_x } + sd_regoffset, { savefregp } + sd_regoffset, { savefregp_x } sd_none, { pushframe } + sd_none, { setfp } + sd_none, { addfp } + sd_offset, { savefplr } + sd_offset, { savefplr_x } + sd_none, { nop } sd_reg, { pushnv } sd_none { savenv } ); diff --git a/compiler/ogcoff.pas b/compiler/ogcoff.pas index 4248149985..03829fae79 100644 --- a/compiler/ogcoff.pas +++ b/compiler/ogcoff.pas @@ -2959,7 +2959,7 @@ const pemagic : array[0..3] of byte = ( objreloc:TObjRelocation; i,j:longint; begin - if target_info.system<>system_x86_64_win64 then + if not (target_info.system in [system_x86_64_win64,system_aarch64_win64]) then exit; exesec:=FindExeSection('.pdata'); if exesec=nil then @@ -2989,7 +2989,10 @@ const pemagic : array[0..3] of byte = ( .eh_frame sections. } break; end; - inc(j,3); + if target_info.system=system_aarch64_win64 then + inc(j,2) + else + inc(j,3); end; end; end; diff --git a/compiler/psub.pas b/compiler/psub.pas index 95f80a2384..c39a175cd6 100644 --- a/compiler/psub.pas +++ b/compiler/psub.pas @@ -1973,7 +1973,12 @@ implementation cg.set_regalloc_live_range_direction(rad_forward); if assigned(finalize_procinfo) then - generate_exceptfilter(tcgprocinfo(finalize_procinfo)) + begin + if target_info.system in [system_aarch64_win64] then + tcgprocinfo(finalize_procinfo).store_tempflags + else + generate_exceptfilter(tcgprocinfo(finalize_procinfo)); + end else if not temps_finalized then begin hlcg.gen_finalize_code(templist); diff --git a/compiler/x86_64/win64unw.pas b/compiler/x86_64/win64unw.pas index d040b4935b..31feb2a13b 100644 --- a/compiler/x86_64/win64unw.pas +++ b/compiler/x86_64/win64unw.pas @@ -397,6 +397,8 @@ begin ash_pushnv, ash_savenv: internalerror(2019050712); + else + internalerror(2020041901); end; end;