dbgllvm: support for record field debug information

This commit is contained in:
Jonas Maebe 2022-06-06 23:12:25 +02:00
parent b456833b03
commit 706443c354
2 changed files with 234 additions and 22 deletions

View File

@ -121,6 +121,7 @@ interface
procedure appenddef_enum(list:TAsmList;def:tenumdef);override; procedure appenddef_enum(list:TAsmList;def:tenumdef);override;
procedure appenddef_array(list:TAsmList;def:tarraydef);override; procedure appenddef_array(list:TAsmList;def:tarraydef);override;
procedure appenddef_record_named(list: TAsmList; fordef: tdef; def: trecorddef; const name: TSymStr); 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_record(list:TAsmList;def:trecorddef);override;
procedure appenddef_pointer(list:TAsmList;def:tpointerdef);override; procedure appenddef_pointer(list:TAsmList;def:tpointerdef);override;
procedure appenddef_formal(list:TAsmList;def:tformaldef); override; procedure appenddef_formal(list:TAsmList;def:tformaldef); override;
@ -159,8 +160,6 @@ interface
function symname(sym: tsym; manglename: boolean): TSymStr; virtual; function symname(sym: tsym; manglename: boolean): TSymStr; virtual;
function visibilitydiflag(vis: tvisibility): TSymStr; function visibilitydiflag(vis: tvisibility): TSymStr;
procedure enum_membersyms_callback(p:TObject;arg:pointer);
procedure ensuremetainit; procedure ensuremetainit;
procedure resetfornewmodule; procedure resetfornewmodule;
@ -486,22 +485,6 @@ implementation
end; 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; procedure TDebugInfoLLVM.ensuremetainit;
begin begin
if not assigned(fllvm_dbg_addr_pd) then 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); procedure TDebugInfoLLVM.appenddef_record_named(list:TAsmList; fordef: tdef; def:trecorddef; const name: TSymStr);
var var
dinode: tai_llvmspecialisedmetadatanode; dinode: tai_llvmspecialisedmetadatanode;
cappedsize: asizeuint;
begin begin
dinode:=def_set_meta_impl(fordef,tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DICompositeType)); dinode:=def_set_meta_impl(fordef,tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DICompositeType));
dinode.addint64('tag',ord(DW_TAG_structure_type)); dinode.addint64('tag',ord(DW_TAG_structure_type));
if (name<>'') then if (name<>'') then
dinode.addstring('name',name); dinode.addstring('name',name);
if def.size<(qword(1) shl 61) then if is_packed_record_or_object(fordef) then
dinode.addqword('size',def.size*8) cappedsize:=def.size
else if def.size<(qword(1) shl 61) then
cappedsize:=def.size*8
else else
{ LLVM internally "only" supports sizes up to 1 shl 61, because they { 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 store all sizes in bits in a qword; the rationale is that there
is no hardware supporting a full 64 bit address space either } 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); 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); write_symtable_procdefs(current_asmdata.asmlists[al_dwarf_info],def.symtable);
end; 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); procedure TDebugInfoLLVM.appenddef_pointer(list:TAsmList;def:tpointerdef);
var var
dinode: tai_llvmspecialisedmetadatanode; dinode: tai_llvmspecialisedmetadatanode;

View File

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