+ Generate unwind bytecode for function prologues on win64.

* For now placed actual processing of unwind info under {$ifdef TEST_WIN64_UNWIND}, because in the current state it doesn't add much value.

git-svn-id: trunk@19200 -
This commit is contained in:
sergei 2011-09-23 21:22:25 +00:00
parent dff5ac3b0a
commit b997094755
9 changed files with 115 additions and 5 deletions

View File

@ -1238,6 +1238,7 @@ implementation
ait_seh_directive :
begin
{$ifdef TEST_WIN64_UNWIND}
AsmWrite('.'+sehdirectivestr[tai_seh_directive(hp).kind]);
case tai_seh_directive(hp).datatype of
sd_none:;
@ -1252,6 +1253,7 @@ implementation
tostr(tai_seh_directive(hp).data.offset));
end;
AsmLn;
{$endif TEST_WIN64_UNWIND}
end;
else

View File

@ -1393,8 +1393,10 @@ Implementation
ait_cutobject :
if SmartAsm then
break;
{$ifdef TEST_WIN64_UNWIND}
ait_seh_directive :
tai_seh_directive(hp).generate_code(objdata);
{$endif TEST_WIN64_UNWIND}
end;
hp:=Tai(hp.next);
end;

View File

@ -466,7 +466,9 @@ interface
{ dfa was generated for this proc }
pi_dfaavailable,
{ subroutine contains interprocedural used labels }
pi_has_interproclabel
pi_has_interproclabel,
{ subroutine has unwind info (win64) }
pi_has_unwind_info
);
tprocinfoflags=set of tprocinfoflag;

View File

@ -95,6 +95,12 @@ unit procinfo;
{ Holds the reference used to store all saved registers. }
save_regs_ref : treference;
{ Last assembler instruction of procedure prologue }
endprologue_ai : tlinkedlistitem;
{ Amount of stack adjustment after all alignments }
final_localsize : longint;
{ Labels for TRUE/FALSE condition, BREAK and CONTINUE }
CurrBreakLabel,
CurrContinueLabel,
@ -183,7 +189,6 @@ implementation
CurrContinueLabel:=nil;
CurrTrueLabel:=nil;
CurrFalseLabel:=nil;
maxpushedparasize:=0;
if Assigned(parent) and (parent.procdef.parast.symtablelevel>=normal_function_level) then
parent.addnestedproc(Self);
end;

View File

@ -1174,6 +1174,9 @@ implementation
{ Add save and restore of used registers }
current_filepos:=entrypos;
gen_save_used_regs(templist);
{ Remember the last instruction of register saving block
(may be =nil for e.g. assembler procedures) }
current_procinfo.endprologue_ai:=templist.last;
aktproccode.insertlistafter(headertai,templist);
current_filepos:=exitpos;
gen_restore_used_regs(aktproccode);

View File

@ -1018,7 +1018,9 @@ const
(mask:pi_dfaavailable;
str:' dfa was generated for this proc '),
(mask:pi_has_interproclabel;
str:' subroutine contains interprocedural used labels ')
str:' subroutine contains interprocedural used labels '),
(mask:pi_has_unwind_info;
str:' unwinding info was generated for this proc ')
);
var
procinfooptions : tprocinfoflags;

View File

@ -2175,11 +2175,23 @@ unit cgx86;
inc(stackmisalignment,sizeof(pint));
include(rg[R_INTREGISTER].preserved_by_proc,RS_FRAME_POINTER_REG);
list.concat(Taicpu.op_reg(A_PUSH,tcgsize2opsize[OS_ADDR],NR_FRAME_POINTER_REG));
if (target_info.system=system_x86_64_win64) then
begin
list.concat(cai_seh_directive.create_reg(ash_pushreg,NR_FRAME_POINTER_REG));
include(current_procinfo.flags,pi_has_unwind_info);
end;
{ Return address and FP are both on stack }
current_asmdata.asmcfi.cfa_def_cfa_offset(list,2*sizeof(pint));
current_asmdata.asmcfi.cfa_offset(list,NR_FRAME_POINTER_REG,-(2*sizeof(pint)));
list.concat(Taicpu.op_reg_reg(A_MOV,tcgsize2opsize[OS_ADDR],NR_STACK_POINTER_REG,NR_FRAME_POINTER_REG));
current_asmdata.asmcfi.cfa_def_cfa_register(list,NR_FRAME_POINTER_REG);
{
TODO: current framepointer handling is not compatible with Win64 at all:
Win64 expects FP to point to the top or into the middle of local area.
In FPC it points to the bottom, making it impossible to generate
UWOP_SET_FPREG unwind code if local area is > 240 bytes.
So for now pretend we never have a framepointer.
}
end;
{ allocate stackframe space }
@ -2194,6 +2206,12 @@ unit cgx86;
cg.g_stackpointer_alloc(list,localsize);
if current_procinfo.framepointer=NR_STACK_POINTER_REG then
current_asmdata.asmcfi.cfa_def_cfa_offset(list,localsize+sizeof(pint));
current_procinfo.final_localsize:=localsize;
if (target_info.system=system_x86_64_win64) then
begin
list.concat(cai_seh_directive.create_offset(ash_stackalloc,localsize));
include(current_procinfo.flags,pi_has_unwind_info);
end;
end;
end;
end;

View File

@ -37,6 +37,7 @@ unit cgcpu;
procedure init_register_allocators;override;
procedure done_register_allocators;override;
procedure g_proc_entry(list : TAsmList; parasize:longint; nostackframe:boolean);override;
procedure g_proc_exit(list : TAsmList;parasize:longint;nostackframe:boolean);override;
procedure g_intf_wrapper(list: TAsmList; procdef: tprocdef; const labelname: string; ioffset: longint);override;
@ -49,7 +50,7 @@ unit cgcpu;
implementation
uses
globtype,globals,verbose,systems,cutils,
globtype,globals,verbose,systems,cutils,cclasses,
symsym,defutil,paramgr,fmodule,
rgobj,tgobj,rgcpu;
@ -110,10 +111,71 @@ unit cgcpu;
setlength(saved_mm_registers,0);
end;
procedure tcgx86_64.g_proc_entry(list : TAsmList;parasize:longint;nostackframe:boolean);
var
hitem: tlinkedlistitem;
r: integer;
href: treference;
templist: TAsmList;
frame_offset: longint;
begin
hitem:=list.last;
inherited g_proc_entry(list,parasize,nostackframe);
if not (pi_has_unwind_info in current_procinfo.flags) then
exit;
{ Generate unwind data for x86_64-win64 }
list.insertafter(cai_seh_directive.create_name(ash_proc,current_procinfo.procdef.mangledname),hitem);
templist:=TAsmList.Create;
{ We need to record postive offsets from RSP; if registers are saved
at negative offsets from RBP we need to account for it. }
if current_procinfo.framepointer=NR_FRAME_POINTER_REG then
frame_offset:=current_procinfo.final_localsize
else
frame_offset:=0;
{ There's no need to describe position of register saves precisely;
since registers are not modified before they are saved, and saves do not
change RSP, 'logically' all saves can happen at the end of prologue. }
href:=current_procinfo.save_regs_ref;
for r:=low(saved_standard_registers) to high(saved_standard_registers) do
if saved_standard_registers[r] in rg[R_INTREGISTER].used_in_proc then
begin
templist.concat(cai_seh_directive.create_reg_offset(ash_savereg,
newreg(R_INTREGISTER,saved_standard_registers[r],R_SUBWHOLE),
href.offset+frame_offset));
inc(href.offset,sizeof(aint));
end;
if uses_registers(R_MMREGISTER) then
begin
if (href.offset mod tcgsize2size[OS_VECTOR])<>0 then
inc(href.offset,tcgsize2size[OS_VECTOR]-(href.offset mod tcgsize2size[OS_VECTOR]));
for r:=low(saved_mm_registers) to high(saved_mm_registers) do
begin
if saved_mm_registers[r] in rg[R_MMREGISTER].used_in_proc then
begin
templist.concat(cai_seh_directive.create_reg_offset(ash_savexmm,
newreg(R_MMREGISTER,saved_mm_registers[r],R_SUBNONE),
href.offset+frame_offset));
inc(href.offset,tcgsize2size[OS_VECTOR]);
end;
end;
end;
templist.concat(cai_seh_directive.create(ash_endprologue));
if assigned(current_procinfo.endprologue_ai) then
current_procinfo.aktproccode.insertlistbefore(current_procinfo.endprologue_ai,templist)
else
list.concatlist(templist);
templist.free;
end;
procedure tcgx86_64.g_proc_exit(list : TAsmList;parasize:longint;nostackframe:boolean);
var
stacksize : longint;
href : treference;
begin
{ Release PIC register }
if cs_create_pic in current_settings.moduleswitches then
@ -135,12 +197,24 @@ unit cgcpu;
if (stacksize<>0) then
cg.a_op_const_reg(list,OP_ADD,OS_ADDR,stacksize,current_procinfo.framepointer);
end
else if (target_info.system=system_x86_64_win64) then
begin
{ Comply with Win64 unwinding mechanism, which only recognizes
'add $constant,%rsp' and 'lea offset(FPREG),%rsp' as belonging to
the function epilog.
Neither 'leave' nor even 'mov %FPREG,%rsp' are allowed. }
reference_reset_base(href,current_procinfo.framepointer,0,sizeof(pint));
list.concat(Taicpu.op_ref_reg(A_LEA,tcgsize2opsize[OS_ADDR],href,NR_STACK_POINTER_REG));
list.concat(Taicpu.op_reg(A_POP,tcgsize2opsize[OS_ADDR],current_procinfo.framepointer));
end
else
list.concat(Taicpu.op_none(A_LEAVE,S_NO));
list.concat(tai_regalloc.dealloc(NR_FRAME_POINTER_REG,nil));
end;
list.concat(Taicpu.Op_none(A_RET,S_NO));
if (pi_has_unwind_info in current_procinfo.flags) then
list.concat(cai_seh_directive.create(ash_endproc));
end;

View File

@ -234,7 +234,7 @@ begin
FElements.Clear;
objdata.createsection(sec_pdata,FName^);
objdata.createsection(sec_pdata,lower(FName^));
pdatasym:=objdata.symboldefine('$pdata$'+FName^,AB_LOCAL,AT_DATA);
objdata.writereloc(0,4,FFrameStartSym,RELOC_RVA);
objdata.writereloc(FFrameStartSym.Size,4,FFrameStartSym,RELOC_RVA);
@ -268,6 +268,8 @@ end;
procedure TWin64CFI.end_prologue(objdata:TObjData);
begin
if not assigned(FName) then
internalerror(2011072312);
FPrologueEndPos:=objdata.CurrObjSec.Size;
end;