DWARF: Lineinfo for "finally" with Windows SEH-64 is wrong ( **partial patch attached** )
FPC 3.3.1 (also any earlier, since SEH64 was added). ``` procedure bar(s: AnsiString); begin write(s); try write; finally // same line as "try" write; end; // prologue (first line) of the finally sub-routine write; end; ``` FPC will generate lineinfo that has - `finally` at the same line as `try` (this includes the implicit finally in the `end` statement) - the prologue of the `$fin_$` procedure containing the actual finally code has the line number of the `end` of the finally block. This causes all kind of forward/backward jumps while debugging ---- The patch below fixes most of it. - `finally` lines will be correct (including the implicit in `end` - $fin subroutines prologue will have the line number of the first line of code (this could be argued, if it should instead use the `finally` line?) #### It does not yet fix The implicit finally $fin subroutine for dec-ref local vars. This has its actual code (not the prologue, but the code after) on the line number of the `begin` but should be at the `end` line. Edit: patch is obsolete, see https://gitlab.com/martin_frb/fpc-src/-/compare/main...mfr%2Fdwarf-seh64-finally-linenum?from_project_id=28644964 <details> <summary>Click to expand / old patch</summary> ``` diff --git a/compiler/nflw.pas b/compiler/nflw.pas index 768c56f1f5..f68b7047cc 100644 --- a/compiler/nflw.pas +++ b/compiler/nflw.pas @@ -30,7 +30,7 @@ interface cclasses, node,cpubase, symconst,symtype,symbase,symdef,symsym, - optloop; + optloop,globtype; type { flags used by loop nodes } @@ -242,6 +242,7 @@ ttryexceptnodeclass = class of ttryexceptnode; one in case no exception occurs } ttryfinallynode = class(ttertiarynode) implicitframe : boolean; + fileinfo_fin : tfileposinfo; constructor create(l,r:tnode);virtual;reintroduce; constructor create_implicit(l,r:tnode);virtual; function pass_typecheck:tnode;override; @@ -297,7 +298,7 @@ tonnodeclass = class of tonnode; implementation uses - globtype,systems,constexp,compinnr, + systems,constexp,compinnr, cutils,verbose,globals,ppu, symtable,paramgr,defcmp,defutil,htypechk,pass_1, ncal,nadd,ncon,nmem,nld,ncnv,nbas,nutils,ninl,nset,ngenutil, @@ -2802,6 +2803,7 @@ tassignmentquery = record inherited create(tryfinallyn,l,r,nil); third:=nil; implicitframe:=false; + fileinfo_fin:=current_filepos; end; @@ -2810,6 +2812,7 @@ tassignmentquery = record inherited create(tryfinallyn,l,r,nil); third:=nil; implicitframe:=true; + fileinfo_fin:=current_filepos; end; @@ -2881,6 +2884,7 @@ tassignmentquery = record begin result:=inherited dogetcopy; ttryfinallynode(result).implicitframe:=implicitframe; + ttryfinallynode(result).fileinfo_fin:=fileinfo_fin; end; diff --git a/compiler/pstatmnt.pas b/compiler/pstatmnt.pas index 2be25101bd..3bf4f40a1b 100644 --- a/compiler/pstatmnt.pas +++ b/compiler/pstatmnt.pas @@ -896,7 +896,7 @@ ((p.resultdef.typ=recorddef) or t:ttoken; unit_found:boolean; oldcurrent_exceptblock: integer; - filepostry : tfileposinfo; + filepostry,filepos_fin : tfileposinfo; begin p_default:=nil; p_specific:=nil; @@ -931,6 +931,7 @@ ((p.resultdef.typ=recorddef) or end; p_try_block:=cblocknode.create(first); + filepos_fin:=current_filepos; if try_to_consume(_FINALLY) then begin inc(exceptblockcounter); @@ -938,6 +939,7 @@ ((p.resultdef.typ=recorddef) or p_finally_block:=statements_til_end; try_statement:=ctryfinallynode.create(p_try_block,p_finally_block); try_statement.fileinfo:=filepostry; + ttryfinallynode(try_statement).fileinfo_fin:=filepos_fin; end else begin diff --git a/compiler/psub.pas b/compiler/psub.pas index 1a1e849754..6a180d5b85 100644 --- a/compiler/psub.pas +++ b/compiler/psub.pas @@ -874,7 +874,7 @@ implementation newblock : tnode; codestatement, newstatement : tstatementnode; - oldfilepos : tfileposinfo; + oldfilepos: tfileposinfo; is_constructor: boolean; begin is_constructor:=assigned(procdef.struct) and @@ -953,6 +953,7 @@ implementation current_filepos:=entrypos; wrappedbody:=ctryfinallynode.create_implicit(code,finalcode); + ttryfinallynode(wrappedbody).fileinfo_fin:=exitpos; { afterconstruction must be called after finalizetemps, because it has to execute after the temps have been finalised in case of a refcounted class (afterconstruction decreases the refcount diff --git a/compiler/x86_64/nx64flw.pas b/compiler/x86_64/nx64flw.pas index 50fb9d09f6..19b4a46596 100644 --- a/compiler/x86_64/nx64flw.pas +++ b/compiler/x86_64/nx64flw.pas @@ -167,6 +167,7 @@ constructor tx64tryfinallynode.create(l, r: TNode); 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)); + finalizepi.procdef.fileinfo := r.fileinfo; // the prologue of the $fin$ routine will get the line number of the first statement (rather than the last) { 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 @@ -185,6 +186,7 @@ constructor tx64tryfinallynode.create_implicit(l, r: TNode); InternalError(2013012501); finalizepi:=tcgprocinfo(current_procinfo.create_for_outlining('$fin$',current_procinfo.procdef.struct,potype_exceptfilter,voidtype,r)); + finalizepi.procdef.fileinfo := r.fileinfo; // the prologue of the $fin$ routine will get the line number of the first statement (rather than the last) 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); @@ -213,6 +215,7 @@ function tx64tryfinallynode.dogetcopy: tnode; begin n.finalizepi.code:=finalizepi.code.getcopy; n.right:=ccallnode.create(nil,tprocsym(n.finalizepi.procdef.procsym),nil,nil,[],nil); + n.right.fileinfo := n.fileinfo_fin; firstpass(n.right); end; end; @@ -233,6 +236,7 @@ function tx64tryfinallynode.simplify(forinline: boolean): tnode; finalizepi.code:=right; foreachnodestatic(right,@copy_parasize,finalizepi); right:=ccallnode.create(nil,tprocsym(finalizepi.procdef.procsym),nil,nil,[],nil); + right.fileinfo := fileinfo_fin; 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 @@ -268,6 +272,7 @@ procedure tx64tryfinallynode.pass_generate_code; oldexitlabel: tasmlabel; oldflowcontrol: tflowcontrol; catch_frame: boolean; + oldpos: tfileposinfo; begin if (target_info.system<>system_x86_64_win64) then begin @@ -330,6 +335,7 @@ procedure tx64tryfinallynode.pass_generate_code; { 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); @@ -352,10 +358,12 @@ procedure tx64tryfinallynode.pass_generate_code; else begin { same as emit_nop but using finallylabel instead of dummy } + current_filepos:=right.fileinfo; cg.a_label(current_asmdata.CurrAsmList,finallylabel); finallylabel.increfs; current_asmdata.CurrAsmList.concat(Taicpu.op_none(A_NOP,S_NO)); cg.a_label(current_asmdata.CurrAsmList,endtrylabel); + right.fileinfo:=current_filepos; end; { i32913 - if the try..finally block is also inside a try..finally or ``` </details>
issue