Browse Source

dbgllvm: support for record field debug information

Jonas Maebe 3 years ago
parent
commit
706443c354
2 changed files with 234 additions and 22 deletions
  1. 225 22
      compiler/llvm/dbgllvm.pas
  2. 9 0
      compiler/symsym.pas

+ 225 - 22
compiler/llvm/dbgllvm.pas

@@ -121,6 +121,7 @@ interface
         procedure appenddef_enum(list:TAsmList;def:tenumdef);override;
         procedure appenddef_array(list:TAsmList;def:tarraydef);override;
         procedure appenddef_record_named(list: TAsmList; fordef: tdef; def: trecorddef; const name: TSymStr);
+        procedure appenddef_struct_fields(list: TAsmlist; def: tabstractrecorddef; defdinode: tai_llvmspecialisedmetadatanode; initialfieldlist: tai_llvmunnamedmetadatanode; cappedsize: asizeuint);
         procedure appenddef_record(list:TAsmList;def:trecorddef);override;
         procedure appenddef_pointer(list:TAsmList;def:tpointerdef);override;
         procedure appenddef_formal(list:TAsmList;def:tformaldef); override;
@@ -159,8 +160,6 @@ interface
         function symname(sym: tsym; manglename: boolean): TSymStr; virtual;
         function visibilitydiflag(vis: tvisibility): TSymStr;
 
-        procedure enum_membersyms_callback(p:TObject;arg:pointer);
-
         procedure ensuremetainit;
         procedure resetfornewmodule;
 
@@ -486,22 +485,6 @@ implementation
       end;
 
 
-    procedure TDebugInfoLLVM.enum_membersyms_callback(p:TObject; arg: pointer);
-      begin
-(*
-        case tsym(p).typ of
-          fieldvarsym:
-            appendsym_fieldvar(pmembercallbackinfo(arg)^.list,pmembercallbackinfo(arg)^.structnode,tfieldvarsym(p));
-          propertysym:
-            appendsym_property(pmembercallbackinfo(arg)^.list,pmembercallbackinfo(arg)^.structnode,tpropertysym(p));
-          constsym:
-            appendsym_const_member(pmembercallbackinfo(arg)^.list,pmembercallbackinfo(arg)^.structnode,tconstsym(p),true);
-          else
-            ;
-        end;
-*)
-      end;
-
     procedure TDebugInfoLLVM.ensuremetainit;
       begin
         if not assigned(fllvm_dbg_addr_pd) then
@@ -1065,26 +1048,246 @@ implementation
     procedure TDebugInfoLLVM.appenddef_record_named(list:TAsmList; fordef: tdef; def:trecorddef; const name: TSymStr);
       var
         dinode: tai_llvmspecialisedmetadatanode;
+        cappedsize: asizeuint;
       begin
         dinode:=def_set_meta_impl(fordef,tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DICompositeType));
         dinode.addint64('tag',ord(DW_TAG_structure_type));
         if (name<>'') then
           dinode.addstring('name',name);
-        if def.size<(qword(1) shl 61) then
-          dinode.addqword('size',def.size*8)
+        if is_packed_record_or_object(fordef) then
+          cappedsize:=def.size
+        else if def.size<(qword(1) shl 61) then
+          cappedsize:=def.size*8
         else
           { LLVM internally "only" supports sizes up to 1 shl 61, because they
             store all sizes in bits in a qword; the rationale is that there
             is no hardware supporting a full 64 bit address space either }
-          dinode.addqword('size',qword(1) shl 61);
+          cappedsize:=qword(1) shl 61;
+        dinode.addqword('size',cappedsize);
 
         list.concat(dinode);
 
-//        def.symtable.symList.ForEachCall(@enum_membersyms_callback,dinode);
+        appenddef_struct_fields(list,def,dinode,tai_llvmunnamedmetadatanode.create,cappedsize);
         write_symtable_procdefs(current_asmdata.asmlists[al_dwarf_info],def.symtable);
       end;
 
 
+    procedure TDebugInfoLLVM.appenddef_struct_fields(list: TAsmlist; def: tabstractrecorddef; defdinode: tai_llvmspecialisedmetadatanode; initialfieldlist: tai_llvmunnamedmetadatanode; cappedsize: asizeuint);
+
+      { returns whether we need to create a nested struct in the variant to hold
+        multiple successive fields, or whether the next field starts at the
+        same offset as the current one. I.e., it returns false for
+          case byte of
+            0: (b: byte);
+            1: (l: longint);
+          end
+
+        but true for
+
+          case byte of
+            0: (b1,b2: byte);
+          end
+
+        and
+
+          case byte of
+            0: (b1: byte;
+                case byte of 0:
+                  b2: byte;
+               )
+          end
+      }
+      function variantfieldstartsnewstruct(field: tfieldvarsym; recst: tabstractrecordsymtable; fieldidx: longint): boolean;
+        var
+          nextfield: tfieldvarsym;
+        begin
+          result:=false;
+          inc(fieldidx);
+          if fieldidx>=recst.symlist.count then
+            exit;
+          { can't have properties or procedures between to start fields of the
+            same variant }
+          if tsym(recst.symlist[fieldidx]).typ<>fieldvarsym then
+            exit;
+          nextfield:=tfieldvarsym(recst.symlist[fieldidx]);
+          if nextfield.fieldoffset=field.fieldoffset then
+            exit;
+          result:=true;
+        end;
+
+      type
+        tvariantinfo = record
+          startfield: tfieldvarsym;
+          uniondi: tai_llvmspecialisedmetadatanode;
+          variantfieldlist: tai_llvmunnamedmetadatanode;
+          curvariantstructfieldlist: tai_llvmunnamedmetadatanode;
+        end;
+        pvariantinfo = ^tvariantinfo;
+
+      function bitoffsetfromvariantstart(field: tfieldvarsym; variantinfolist: tfplist; totalbitsize: ASizeUInt): qword;
+        var
+          variantstartfield: tfieldvarsym;
+        begin
+          if not assigned(variantinfolist) then
+            begin
+              result:=field.bitoffset;
+              exit;
+            end;
+          result:=0;
+          if vo_is_first_field in field.varoptions then
+            exit;
+          variantstartfield:=pvariantinfo(variantinfolist[variantinfolist.count-1])^.startfield;
+          { variant fields always start on a byte boundary, so no need for
+            rounding/truncating }
+          result:=field.bitoffset-variantstartfield.bitoffset;
+        end;
+
+      var
+        variantinfolist: tfplist;
+        variantinfo: pvariantinfo;
+        recst: trecordsymtable;
+        scope,
+        fielddi,
+        uniondi,
+        structdi: tai_llvmspecialisedmetadatanode;
+        fieldlist: tai_llvmunnamedmetadatanode;
+        i, varindex: longint;
+        field: tfieldvarsym;
+        bitoffset: asizeuint;
+        bpackedrecst: boolean;
+      begin
+        recst:=trecordsymtable(def.symtable);
+        bpackedrecst:=recst.fieldalignment=bit_alignment;
+        scope:=defdinode;
+        variantinfolist:=nil;
+
+        fieldlist:=initialfieldlist;
+        list.concat(fieldlist);
+        defdinode.addmetadatarefto('elements',fieldlist);
+
+        for i:=0 to recst.symlist.count-1 do
+          begin
+            if (tsym(recst.symlist[i]).typ<>fieldvarsym) then
+              continue;
+
+            field:=tfieldvarsym(recst.symlist[i]);
+            { start of a new variant part? }
+            if vo_is_first_field in field.varoptions then
+              begin
+                if not assigned(variantinfolist) then
+                  begin
+                    variantinfolist:=tfplist.create;
+                  end;
+                varindex:=variantinfolist.count-1;
+                if (varindex=-1) or
+                   (pvariantinfo(variantinfolist[varindex])^.startfield.fieldoffset<field.fieldoffset) then
+                  begin
+                    { more deeply nested variant }
+                    uniondi:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DICompositeType);
+                    list.concat(uniondi);
+                    fieldlist.addvalue(llvm_getmetadatareftypedconst(uniondi));
+
+                    uniondi.addenum('tag','DW_TAG_union_type');
+                    uniondi.addmetadatarefto('scope',scope);
+                    try_add_file_metaref(uniondi,field.fileinfo,false);
+                    { the size of this variant part is the total size of the
+                      record minus the start of this field; not 100% correct
+                      in case of multiple parallel nested variants, but not
+                      really important since it's all padding anyway }
+                    uniondi.addint64('size',cappedsize-min(field.bitoffset,cappedsize));
+                    fieldlist:=tai_llvmunnamedmetadatanode.create;
+                    list.concat(fieldlist);
+                    uniondi.addmetadatarefto('elements',fieldlist);
+
+                    scope:=uniondi;
+
+                    new(variantinfo);
+                    variantinfo^.startfield:=field;
+                    variantinfo^.uniondi:=uniondi;
+                    variantinfo^.variantfieldlist:=fieldlist;
+                    variantinfo^.curvariantstructfieldlist:=nil;
+
+                    variantinfolist.Add(variantinfo);
+                    inc(varindex);
+                  end
+                else
+                  begin
+                    {finalise more deeply nested variants }
+                    while (varindex>=0) and
+                          (pvariantinfo(variantinfolist[varindex])^.startfield.fieldoffset>field.fieldoffset) do
+                      begin
+                        dispose(pvariantinfo(variantinfolist[varindex]));
+                        dec(varindex);
+                      end;
+                    if (varindex<0) then
+                      internalerror(2022060610);
+                    variantinfo:=pvariantinfo(variantinfolist[varindex]);
+                    if variantinfo^.startfield.fieldoffset<>field.fieldoffset then
+                      internalerror(2022060611);
+
+                    { a variant part is always the last part -> end of previous
+                      struct, if any}
+                    variantinfo^.curvariantstructfieldlist:=nil;
+
+                    fieldlist:=variantinfo^.variantfieldlist;
+                    scope:=variantinfo^.uniondi;
+
+                    { variant at the same level as a previous one }
+                    variantinfolist.count:=varindex+1;
+                  end;
+
+                if not variantfieldstartsnewstruct(field,recst,i) then
+                  begin
+                    variantinfo^.curvariantstructfieldlist:=nil;
+                    fieldlist:=variantinfo^.variantfieldlist;
+                    scope:=variantinfo^.uniondi;
+                  end
+                else
+                  begin
+                    structdi:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DICompositeType);
+                    list.concat(structdi);
+                    structdi.addenum('tag','DW_TAG_structure_type');
+                    structdi.addmetadatarefto('scope',variantinfo^.uniondi);
+                    structdi.addint64('size',cappedsize-min(field.bitoffset,cappedsize));
+                    variantinfo^.curvariantstructfieldlist:=tai_llvmunnamedmetadatanode.create;
+                    list.concat(variantinfo^.curvariantstructfieldlist);
+                    structdi.addmetadatarefto('elements',variantinfo^.curvariantstructfieldlist);
+                    fieldlist.addvalue(llvm_getmetadatareftypedconst(structdi));
+
+                    fieldlist:=variantinfo^.curvariantstructfieldlist;
+                    scope:=structdi;
+                  end;
+              end;
+
+            fielddi:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DIDerivedType);
+            fielddi.addenum('tag','DW_TAG_member');
+            fielddi.addstring('name',symname(field,false));
+            fielddi.addmetadatarefto('scope',scope);
+            try_add_file_metaref(fielddi,field.fileinfo,false);
+            fielddi.addmetadatarefto('baseType',def_meta_node(field.vardef));
+            if bpackedrecst and
+               is_ordinal(field.vardef) then
+              fielddi.addqword('size',field.getpackedbitsize)
+            else
+              fielddi.addqword('size',min(asizeuint(field.getsize)*8,cappedsize));
+            bitoffset:=bitoffsetfromvariantstart(field,variantinfolist,cappedsize);
+            if bitoffset<>0 then
+              fielddi.addqword('offset',bitoffset);
+
+            fieldlist.addvalue(llvm_getmetadatareftypedconst(fielddi));
+            list.concat(fielddi);
+          end;
+        if assigned(variantinfolist) then
+          begin
+            for i:=0 to variantinfolist.count-1 do
+              begin
+                dispose(pvariantinfo(variantinfolist[i]));
+              end;
+          end;
+        variantinfolist.free;
+      end;
+
+
     procedure TDebugInfoLLVM.appenddef_pointer(list:TAsmList;def:tpointerdef);
       var
         dinode: tai_llvmspecialisedmetadatanode;

+ 9 - 0
compiler/symsym.pas

@@ -246,6 +246,7 @@ interface
             override ppuwrite_platform instead }
           procedure ppuwrite(ppufile:tcompilerppufile);override;final;
           procedure set_externalname(const s:string);virtual;
+          function bitoffset: asizeuint;
           function mangledname:TSymStr;override;
           destructor destroy;override;
 {$ifdef DEBUG_NODE_XML}
@@ -2041,6 +2042,14 @@ implementation
       end;
 
 
+    function tfieldvarsym.bitoffset: asizeuint;
+      begin
+        result:=fieldoffset;
+        if tabstractrecordsymtable(owner).fieldalignment<>bit_alignment then
+         result:=result*8;
+      end;
+
+
     function tfieldvarsym.mangledname:TSymStr;
       var
         srsym : tsym;