浏览代码

+ 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 -

sergei 14 年之前
父节点
当前提交
b997094755

+ 2 - 0
compiler/aggas.pas

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

+ 2 - 0
compiler/assemble.pas

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

+ 3 - 1
compiler/globtype.pas

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

+ 6 - 1
compiler/procinfo.pas

@@ -95,6 +95,12 @@ unit procinfo;
           { Holds the reference used to store all saved registers. }
           { Holds the reference used to store all saved registers. }
           save_regs_ref : treference;
           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 }
           { Labels for TRUE/FALSE condition, BREAK and CONTINUE }
           CurrBreakLabel,
           CurrBreakLabel,
           CurrContinueLabel,
           CurrContinueLabel,
@@ -183,7 +189,6 @@ implementation
         CurrContinueLabel:=nil;
         CurrContinueLabel:=nil;
         CurrTrueLabel:=nil;
         CurrTrueLabel:=nil;
         CurrFalseLabel:=nil;
         CurrFalseLabel:=nil;
-        maxpushedparasize:=0;
         if Assigned(parent) and (parent.procdef.parast.symtablelevel>=normal_function_level) then
         if Assigned(parent) and (parent.procdef.parast.symtablelevel>=normal_function_level) then
           parent.addnestedproc(Self);
           parent.addnestedproc(Self);
       end;
       end;

+ 3 - 0
compiler/psub.pas

@@ -1174,6 +1174,9 @@ implementation
             { Add save and restore of used registers }
             { Add save and restore of used registers }
             current_filepos:=entrypos;
             current_filepos:=entrypos;
             gen_save_used_regs(templist);
             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);
             aktproccode.insertlistafter(headertai,templist);
             current_filepos:=exitpos;
             current_filepos:=exitpos;
             gen_restore_used_regs(aktproccode);
             gen_restore_used_regs(aktproccode);

+ 3 - 1
compiler/utils/ppudump.pp

@@ -1018,7 +1018,9 @@ const
          (mask:pi_dfaavailable;
          (mask:pi_dfaavailable;
          str:' dfa was generated for this proc '),
          str:' dfa was generated for this proc '),
          (mask:pi_has_interproclabel;
          (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
 var
   procinfooptions : tprocinfoflags;
   procinfooptions : tprocinfoflags;

+ 18 - 0
compiler/x86/cgx86.pas

@@ -2175,11 +2175,23 @@ unit cgx86;
                 inc(stackmisalignment,sizeof(pint));
                 inc(stackmisalignment,sizeof(pint));
                 include(rg[R_INTREGISTER].preserved_by_proc,RS_FRAME_POINTER_REG);
                 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));
                 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 }
                 { Return address and FP are both on stack }
                 current_asmdata.asmcfi.cfa_def_cfa_offset(list,2*sizeof(pint));
                 current_asmdata.asmcfi.cfa_def_cfa_offset(list,2*sizeof(pint));
                 current_asmdata.asmcfi.cfa_offset(list,NR_FRAME_POINTER_REG,-(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));
                 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);
                 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;
               end;
 
 
             { allocate stackframe space }
             { allocate stackframe space }
@@ -2194,6 +2206,12 @@ unit cgx86;
                 cg.g_stackpointer_alloc(list,localsize);
                 cg.g_stackpointer_alloc(list,localsize);
                 if current_procinfo.framepointer=NR_STACK_POINTER_REG then
                 if current_procinfo.framepointer=NR_STACK_POINTER_REG then
                   current_asmdata.asmcfi.cfa_def_cfa_offset(list,localsize+sizeof(pint));
                   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;
           end;
       end;
       end;

+ 75 - 1
compiler/x86_64/cgcpu.pas

@@ -37,6 +37,7 @@ unit cgcpu;
         procedure init_register_allocators;override;
         procedure init_register_allocators;override;
         procedure done_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_proc_exit(list : TAsmList;parasize:longint;nostackframe:boolean);override;
         procedure g_intf_wrapper(list: TAsmList; procdef: tprocdef; const labelname: string; ioffset: longint);override;
         procedure g_intf_wrapper(list: TAsmList; procdef: tprocdef; const labelname: string; ioffset: longint);override;
 
 
@@ -49,7 +50,7 @@ unit cgcpu;
   implementation
   implementation
 
 
     uses
     uses
-       globtype,globals,verbose,systems,cutils,
+       globtype,globals,verbose,systems,cutils,cclasses,
        symsym,defutil,paramgr,fmodule,
        symsym,defutil,paramgr,fmodule,
        rgobj,tgobj,rgcpu;
        rgobj,tgobj,rgcpu;
 
 
@@ -110,10 +111,71 @@ unit cgcpu;
         setlength(saved_mm_registers,0);
         setlength(saved_mm_registers,0);
       end;
       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);
     procedure tcgx86_64.g_proc_exit(list : TAsmList;parasize:longint;nostackframe:boolean);
       var
       var
         stacksize : longint;
         stacksize : longint;
+        href : treference;
       begin
       begin
         { Release PIC register }
         { Release PIC register }
         if cs_create_pic in current_settings.moduleswitches then
         if cs_create_pic in current_settings.moduleswitches then
@@ -135,12 +197,24 @@ unit cgcpu;
                 if (stacksize<>0) then
                 if (stacksize<>0) then
                   cg.a_op_const_reg(list,OP_ADD,OS_ADDR,stacksize,current_procinfo.framepointer);
                   cg.a_op_const_reg(list,OP_ADD,OS_ADDR,stacksize,current_procinfo.framepointer);
               end
               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
             else
               list.concat(Taicpu.op_none(A_LEAVE,S_NO));
               list.concat(Taicpu.op_none(A_LEAVE,S_NO));
             list.concat(tai_regalloc.dealloc(NR_FRAME_POINTER_REG,nil));
             list.concat(tai_regalloc.dealloc(NR_FRAME_POINTER_REG,nil));
           end;
           end;
 
 
         list.concat(Taicpu.Op_none(A_RET,S_NO));
         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;
       end;
 
 
 
 

+ 3 - 1
compiler/x86_64/win64unw.pas

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