mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-06-11 06:01:25 +02:00

Even though it's supposedly deprecated, clang also still uses it and without the declaration ranges of local variables are sometimes cut off
2973 lines
118 KiB
ObjectPascal
2973 lines
118 KiB
ObjectPascal
{
|
||
Copyright (c) 2021-2022 by Jonas Maebe,
|
||
member of the Free Pascal Compiler development team
|
||
|
||
This units contains support for LLVM debug info generation
|
||
|
||
This program is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 2 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program; if not, write to the Free Software
|
||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
||
****************************************************************************
|
||
}
|
||
{
|
||
This units contains support for LLVM debug info generation.
|
||
|
||
LLVM debug information is stored as metadata in the LLVM bitcode, and is
|
||
loosely based on DWARF (it also reuses some DWARF constants)
|
||
}
|
||
unit dbgllvm;
|
||
|
||
{$i fpcdefs.inc}
|
||
|
||
interface
|
||
|
||
uses
|
||
cclasses,globtype,
|
||
cgbase,
|
||
aasmbase,aasmtai,aasmdata,aasmcnst,aasmllvm,aasmllvmmetadata,
|
||
symbase,symconst,symtype,symdef,symsym,
|
||
finput,
|
||
DbgBase, dbgdwarfconst;
|
||
|
||
type
|
||
TLLVMMetaDefHashSetItem = record
|
||
{ HashSetItem.Data: LLVM metadata which other types reference when
|
||
referring to this type (usually a typedef) }
|
||
HashSetItem: THashSetItem;
|
||
{ in case of a class, the field layout (since a class itself is just a
|
||
pointer }
|
||
struct_metadef: tai_llvmspecialisedmetadatanode;
|
||
{ the metadata actually containing the type definition (usually
|
||
referenced by HashSetItem.Data), filled in by appenddef_* }
|
||
implmetadef: tai_llvmspecialisedmetadatanode;
|
||
end;
|
||
PLLVMMetaDefHashSetItem = ^TLLVMMetaDefHashSetItem;
|
||
|
||
TLLVMMetaDefHashSet = class(THashSet)
|
||
class function SizeOfItem: Integer; override;
|
||
end;
|
||
|
||
TDebugInfoLLVM = class(TDebugInfo)
|
||
strict private
|
||
var
|
||
{ lookup table for def -> LLVMMeta info }
|
||
fdefmeta: TLLVMMetaDefHashSet;
|
||
{ lookup table for file -> LLVMMeta info (DIFile) }
|
||
ffilemeta: THashSet;
|
||
{ lookup table for line,column,scope -> LLVMMeta info (DILocation) }
|
||
flocationmeta: THashSet;
|
||
{ lookup table for scope,file -> LLVMMeta info (DILexicalBlockFile, for include files) }
|
||
flexicalblockfilemeta: THashSet;
|
||
{ lookup table for tstaticvarsym -> taillvmdecl }
|
||
fstaticvarsymdecl: THashSet;
|
||
{ lookup table for local/paravarsym -> metadata }
|
||
flocalvarsymmeta: THashSet;
|
||
|
||
fcunode: tai_llvmspecialisedmetadatanode;
|
||
fenums: tai_llvmunnamedmetadatanode;
|
||
fretainedtypes: tai_llvmunnamedmetadatanode;
|
||
fglobals: tai_llvmunnamedmetadatanode;
|
||
{ reusable empty expression node }
|
||
femptyexpression,
|
||
{ reusable deref node }
|
||
fderefexpression : tai_llvmspecialisedmetadatanode;
|
||
|
||
fllvm_dbg_declare_pd: tprocdef;
|
||
fllvm_dbg_addr_pd: tprocdef;
|
||
|
||
function absolute_llvm_path(const s:tcmdstr):tcmdstr;
|
||
protected
|
||
vardatadef: trecorddef;
|
||
|
||
procedure try_add_file_metaref(dinode: tai_llvmspecialisedmetadatanode; const fileinfo: tfileposinfo; includescope: boolean);
|
||
function add_line_metanode(const fileinfo: tfileposinfo): tai_llvmspecialisedmetadatanode;
|
||
|
||
function def_meta_impl(def: tdef) : tai_llvmspecialisedmetadatanode;
|
||
function def_set_meta_impl(def: tdef; meta_kind: tspecialisedmetadatanodekind): tai_llvmspecialisedmetadatanode;
|
||
function def_meta_class_struct(def: tobjectdef) : tai_llvmspecialisedmetadatanode;
|
||
function def_meta_node(def: tdef): tai_llvmspecialisedmetadatanode;
|
||
function def_meta_ref(def: tdef): tai_simpletypedconst;
|
||
function file_getmetanode(moduleindex: tfileposmoduleindex; fileindex: tfileposfileindex): tai_llvmspecialisedmetadatanode;
|
||
function filepos_getmetanode(const filepos: tfileposinfo; const functionfileinfo: tfileposinfo; const functionscope: tai_llvmspecialisedmetadatanode; nolineinfo: boolean): tai_llvmspecialisedmetadatanode;
|
||
function get_def_metatai(def:tdef): PLLVMMetaDefHashSetItem;
|
||
|
||
procedure staticvarsym_set_decl(sym: tsym; decl: taillvmdecl);
|
||
function staticvarsym_get_decl(sym: tsym): taillvmdecl;
|
||
|
||
function localvarsym_get_meta(sym: tsym; out is_new: boolean): tai_llvmspecialisedmetadatanode;
|
||
|
||
procedure appenddef_array_internal(list: TAsmList; fordef: tdef; eledef: tdef; lowrange, highrange: asizeint);
|
||
function getabstractprocdeftypes(list: TAsmList; def:tabstractprocdef): tai_llvmbasemetadatanode;
|
||
|
||
procedure afterappenddef(list: TAsmList; def: tdef); override;
|
||
procedure appenddef_ord(list:TAsmList;def:torddef);override;
|
||
procedure appenddef_float(list:TAsmList;def:tfloatdef);override;
|
||
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_named(list: TAsmList; def: tabstractrecorddef; structdi: tai_llvmspecialisedmetadatanode; initialfieldlist: tai_llvmunnamedmetadatanode; 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;
|
||
procedure appenddef_string(list:TAsmList;def:tstringdef);override;
|
||
procedure appenddef_procvar(list:TAsmList;def:tprocvardef);override;
|
||
procedure appenddef_file(list:TAsmList;def:tfiledef); override;
|
||
procedure appenddef_object(list:TAsmList;def:tobjectdef); override;
|
||
procedure appenddef_set(list:TAsmList;def:tsetdef); override;
|
||
procedure appenddef_undefined(list:TAsmList;def:tundefineddef); override;
|
||
procedure appenddef_classref(list: TAsmList; def: tclassrefdef); override;
|
||
procedure appenddef_variant(list:TAsmList;def:tvariantdef); override;
|
||
|
||
procedure appendprocdef(list:TAsmList;def:tprocdef);override;
|
||
|
||
procedure adddefinitionlocal(dinode: tai_llvmspecialisedmetadatanode; definition, local, usedispflags: boolean; out dispFlags: tsymstr);
|
||
|
||
function get_symlist_sym_offset(symlist: ppropaccesslistitem; out sym: tabstractvarsym; out offset: pint): boolean;
|
||
procedure appendsym_var(list:TAsmList;sym:tabstractnormalvarsym);
|
||
procedure appendsym_var_with_name_type_offset(list:TAsmList; sym:tabstractnormalvarsym; const name: TSymStr; def: tdef; offset: pint(*; const flags: tdwarfvarsymflags*));
|
||
{ used for fields and properties mapped to fields }
|
||
procedure appendsym_fieldvar_with_name_offset(list:TAsmList;sym: tfieldvarsym;const name: string; def: tdef; offset: pint);
|
||
procedure appendsym_const_member(list:TAsmList;sym:tconstsym;ismember:boolean);
|
||
|
||
procedure beforeappendsym(list:TAsmList;sym:tsym);override;
|
||
procedure appendsym_staticvar(list:TAsmList;sym:tstaticvarsym);override;
|
||
procedure appendsym_paravar(list:TAsmList;sym:tparavarsym);override;
|
||
procedure appendsym_localvar(list:TAsmList;sym:tlocalvarsym);override;
|
||
procedure appendsym_fieldvar(list:TAsmList;sym:tfieldvarsym);override;
|
||
procedure appendsym_const(list:TAsmList;sym:tconstsym);override;
|
||
procedure appendsym_type(list:TAsmList;sym:ttypesym);override;
|
||
procedure appendsym_label(list:TAsmList;sym:tlabelsym);override;
|
||
procedure appendsym_absolute(list:TAsmList;sym:tabsolutevarsym);override;
|
||
procedure appendsym_property(list:TAsmList;sym:tpropertysym);override;
|
||
|
||
function symdebugname(sym:tsym): TSymStr;
|
||
function symname(sym: tsym; manglename: boolean): TSymStr; virtual;
|
||
function visibilitydiflag(vis: tvisibility): TSymStr;
|
||
|
||
procedure ensuremetainit;
|
||
procedure resetfornewmodule;
|
||
|
||
procedure collectglobalsyms;
|
||
procedure updatelocalvardbginfo(hp: taillvm; pd: tprocdef; functionscope: tai_llvmspecialisedmetadatanode);
|
||
public
|
||
constructor Create;override;
|
||
destructor Destroy;override;
|
||
procedure insertmoduleinfo;override;
|
||
procedure inserttypeinfo;override;
|
||
procedure insertlineinfo(list:TAsmList);override;
|
||
function dwarf_version: Word; virtual; abstract;
|
||
end;
|
||
|
||
implementation
|
||
|
||
uses
|
||
sysutils,cutils,cfileutl,constexp,
|
||
version,globals,verbose,systems,
|
||
cpubase,cpuinfo,paramgr,
|
||
fmodule,
|
||
defutil,symtable,symcpu,ppu,
|
||
llvminfo,llvmbase
|
||
;
|
||
|
||
{$push}
|
||
{$scopedenums on}
|
||
type
|
||
TLLVMDIFlags = (
|
||
DIFlagNone = 0,
|
||
DIFlagPrivate = 1,
|
||
DIFlagProtected = 2,
|
||
DIFlagPublic = 3,
|
||
DIFlagFwdDecl = 1 shl 2,
|
||
DIFlagAppleBlock = 1 shl 3,
|
||
DIFlagReservedBit4 = 1 shl 4,
|
||
{ virtual inheritance at the C++ struct level, not at method level; use the SPFlag for that virtual methods) }
|
||
DIFlagVirtual = 1 shl 5,
|
||
DIFlagArtificial = 1 shl 6,
|
||
DIFlagExplicit = 1 shl 7,
|
||
DIFlagPrototyped = 1 shl 8,
|
||
DIFlagObjcClassComplete = 1 shl 9,
|
||
DIFlagObjectPointer = 1 shl 10,
|
||
DIFlagVector = 1 shl 11,
|
||
DIFlagStaticMember = 1 shl 12,
|
||
DIFlagLValueReference = 1 shl 13,
|
||
DIFlagRValueReference = 1 shl 14,
|
||
DIFlagReserved = 1 shl 15,
|
||
DIFlagSingleInheritance = 1 shl 16,
|
||
DIFlagMultipleInheritance = 1 shl 17,
|
||
DIFlagVirtualInheritance = 1 shl 18,
|
||
DIFlagIntroducedVirtual = 1 shl 19,
|
||
DIFlagBitField = 1 shl 20,
|
||
DIFlagNoReturn = 1 shl 21,
|
||
{ at the type level, DWARF 5 DW_CC_pass_by_value }
|
||
DIFlagTypePassByValue = 1 shl 22,
|
||
{ at the type level, DWARF 5 DW_CC_pass_by_reference }
|
||
DIFlagTypePassByReference = 1 shl 23,
|
||
DIFlagEnumClass = 1 shl 24,
|
||
DIFlagThunk = 1 shl 25,
|
||
|
||
{ moved to DISPFlags in LLVM 8.0 }
|
||
DIFlagMainSubprogram_Deprecated = 1 shl 21
|
||
{ introduced/renamed after LLVM 7.0, but nothing we need right now
|
||
,
|
||
DIFlagNonTrivial,
|
||
DIFlagBigEndian,
|
||
DIFlagLittleEndian
|
||
}
|
||
);
|
||
|
||
TLLVMDISPFlags = (
|
||
DISPFlagVirtual = 1,
|
||
DISPFlagPureVirtual = 2,
|
||
DISPFlagLocalToUnit = 1 shl 2,
|
||
DISPFlagDefinition = 1 shl 3,
|
||
DISPFlagOptimized = 1 shl 4,
|
||
DISPFlagPure = 1 shl 5,
|
||
DISPFlagElemental = 1 shl 6,
|
||
DISPFlagRecursive = 1 shl 7,
|
||
DISPFlagMainSubprogram = 1 shl 8,
|
||
DISPFlagDeleted = 1 shl 9,
|
||
DISPFlagObjCDirect = 1 shl 11
|
||
);
|
||
|
||
|
||
{$pop}
|
||
|
||
TLLVMLocationAtom = (
|
||
DW_OP_LLVM_fragment = $1000, ///< Only used in LLVM metadata.
|
||
DW_OP_LLVM_convert = $1001, ///< Only used in LLVM metadata.
|
||
DW_OP_LLVM_tag_offset = $1002, ///< Only used in LLVM metadata.
|
||
DW_OP_LLVM_entry_value = $1003, ///< Only used in LLVM metadata.
|
||
DW_OP_LLVM_implicit_pointer = $1004, ///< Only used in LLVM metadata.
|
||
DW_OP_LLVM_arg = $1005 ///< Only used in LLVM metadata.
|
||
);
|
||
|
||
{****************************************************************************
|
||
TLLVMMetaDefHashSet
|
||
****************************************************************************}
|
||
|
||
class function TLLVMMetaDefHashSet.SizeOfItem: Integer;
|
||
begin
|
||
Result:=sizeof(TLLVMMetaDefHashSetItem);
|
||
end;
|
||
|
||
{****************************************************************************
|
||
TDebugInfoLLVM
|
||
****************************************************************************}
|
||
|
||
function TDebugInfoLLVM.absolute_llvm_path(const s:tcmdstr):tcmdstr;
|
||
begin
|
||
{ Remove trailing / and ./ prefixes and always use a / }
|
||
result:=BsToSlash(ExcludeTrailingPathDelimiter(FixFileName(ExpandFileName(s))));
|
||
end;
|
||
|
||
function TDebugInfoLLVM.get_def_metatai(def:tdef): PLLVMMetaDefHashSetItem;
|
||
begin
|
||
if def.dbg_state=dbg_state_unused then
|
||
def.dbg_state:=dbg_state_used;
|
||
{ Need a new meta item? }
|
||
result:=PLLVMMetaDefHashSetItem(fdefmeta.FindOrAdd(@def,sizeof(def)));
|
||
{ the other fields besides Data are not initialised }
|
||
if not assigned(result^.HashSetItem.Data) then
|
||
begin
|
||
{ will be turned into a pointerdef (in case of Objective-C types) or
|
||
typedef later on. We only really need a typedef if this def has
|
||
a typesym (to add the name), but it allows us to create a generic
|
||
specialised metatype node that can represent any type. Otherwise
|
||
we have to duplicate the logic here to determine whether it's a
|
||
basic, derived or composite type.
|
||
|
||
exception: procdefs because we cannot make typedefs for those}
|
||
if def.typ<>procdef then
|
||
begin
|
||
result^.HashSetItem.Data:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DIDerivedType);
|
||
|
||
if is_implicit_pointer_object_type(def) then
|
||
result^.struct_metadef:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DICompositeType)
|
||
else
|
||
result^.struct_metadef:=nil;
|
||
result^.implmetadef:=nil;
|
||
end
|
||
else
|
||
begin
|
||
result^.HashSetItem.Data:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DISubprogram);
|
||
result^.struct_metadef:=nil;
|
||
result^.implmetadef:=nil;
|
||
end;
|
||
|
||
if def.dbg_state=dbg_state_used then
|
||
deftowritelist.Add(def);
|
||
defnumberlist.Add(def);
|
||
end;
|
||
end;
|
||
|
||
procedure TDebugInfoLLVM.staticvarsym_set_decl(sym: tsym; decl: taillvmdecl);
|
||
var
|
||
entry: PHashSetItem;
|
||
begin
|
||
entry:=fstaticvarsymdecl.FindOrAdd(@sym,sizeof(sym));
|
||
if assigned(entry^.Data) then
|
||
internalerror(2022051701);
|
||
entry^.Data:=decl;
|
||
end;
|
||
|
||
function TDebugInfoLLVM.staticvarsym_get_decl(sym: tsym): taillvmdecl;
|
||
var
|
||
entry: PHashSetItem;
|
||
begin
|
||
result:=nil;
|
||
entry:=fstaticvarsymdecl.Find(@sym,sizeof(sym));
|
||
if assigned(entry) then
|
||
result:=taillvmdecl(entry^.Data);
|
||
end;
|
||
|
||
|
||
function TDebugInfoLLVM.localvarsym_get_meta(sym: tsym; out is_new: boolean): tai_llvmspecialisedmetadatanode;
|
||
var
|
||
entry: PHashSetItem;
|
||
begin
|
||
entry:=fstaticvarsymdecl.FindOrAdd(@sym,sizeof(sym));
|
||
if not assigned(entry^.Data) then
|
||
begin
|
||
result:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DILocalVariable);
|
||
current_asmdata.AsmLists[al_dwarf_info].concat(result);
|
||
entry^.Data:=result;
|
||
is_new:=true;
|
||
exit;
|
||
end;
|
||
is_new:=false;
|
||
result:=tai_llvmspecialisedmetadatanode(entry^.Data);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appenddef_array_internal(list: TAsmList; fordef: tdef; eledef: tdef; lowrange, highrange: asizeint);
|
||
var
|
||
dinode,
|
||
subrangenode: tai_llvmspecialisedmetadatanode;
|
||
arrayrangenode: tai_llvmunnamedmetadatanode;
|
||
begin
|
||
{ range of the array }
|
||
subrangenode:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DISubrange);
|
||
{ include length }
|
||
subrangenode.addqword('lowerBound',lowRange);
|
||
if highrange>=0 then
|
||
subrangenode.addqword('count',qword(highRange)+1)
|
||
else
|
||
subrangenode.addint64('count',highRange+1);
|
||
list.concat(subrangenode);
|
||
{ collection containing the one range }
|
||
arrayrangenode:=tai_llvmunnamedmetadatanode.create;
|
||
arrayrangenode.addvalue(llvm_getmetadatareftypedconst(subrangenode));
|
||
list.concat(arrayrangenode);
|
||
{ the array definition }
|
||
dinode:=def_set_meta_impl(fordef,tspecialisedmetadatanodekind.DICompositeType);
|
||
dinode.addenum('tag','DW_TAG_array_type');
|
||
dinode.addmetadatarefto('baseType',def_meta_node(eledef));
|
||
dinode.addqword('size',eledef.size*(highrange-lowrange+1)*8);
|
||
dinode.addmetadatarefto('elements',arrayrangenode);
|
||
list.concat(dinode);
|
||
end;
|
||
|
||
function TDebugInfoLLVM.getabstractprocdeftypes(list: TAsmList; def: tabstractprocdef): tai_llvmbasemetadatanode;
|
||
var
|
||
types: tai_llvmunnamedmetadatanode;
|
||
i: longint;
|
||
begin
|
||
types:=tai_llvmunnamedmetadatanode.create;
|
||
list.concat(types);
|
||
{ we still need a DISubProgramType in this case, but not the list of types }
|
||
if not(cs_debuginfo in current_settings.moduleswitches) then
|
||
exit;
|
||
if is_void(def.returndef) then
|
||
types.addvalue(tai_simpletypedconst.create(llvm_metadatatype,nil))
|
||
else
|
||
types.addvalue(def_meta_ref(def.returndef));
|
||
for i:=0 to def.paras.count-1 do
|
||
begin
|
||
types.addvalue(def_meta_ref(tparavarsym(def.paras[i]).vardef));
|
||
end;
|
||
result:=types;
|
||
end;
|
||
|
||
function TDebugInfoLLVM.def_meta_impl(def: tdef): tai_llvmspecialisedmetadatanode;
|
||
begin
|
||
if assigned(def.typesym) then
|
||
result:=get_def_metatai(def)^.implmetadef
|
||
else
|
||
result:=def_meta_node(def);
|
||
end;
|
||
|
||
function TDebugInfoLLVM.def_set_meta_impl(def: tdef; meta_kind: tspecialisedmetadatanodekind): tai_llvmspecialisedmetadatanode;
|
||
begin
|
||
if assigned(def.typesym) then
|
||
begin
|
||
result:=tai_llvmspecialisedmetadatanode.create(meta_kind);
|
||
get_def_metatai(def)^.implmetadef:=result
|
||
end
|
||
else
|
||
begin
|
||
result:=def_meta_node(def);
|
||
result.switchkind(meta_kind);
|
||
end;
|
||
end;
|
||
|
||
function TDebugInfoLLVM.def_meta_class_struct(def: tobjectdef): tai_llvmspecialisedmetadatanode;
|
||
begin
|
||
result:=tai_llvmspecialisedmetadatanode(get_def_metatai(def)^.struct_metadef);
|
||
end;
|
||
|
||
function TDebugInfoLLVM.def_meta_node(def: tdef): tai_llvmspecialisedmetadatanode;
|
||
begin
|
||
if not is_void(def) then
|
||
result:=tai_llvmspecialisedmetadatanode(get_def_metatai(def)^.HashSetItem.Data)
|
||
else
|
||
result:=nil;
|
||
end;
|
||
|
||
function TDebugInfoLLVM.def_meta_ref(def: tdef): tai_simpletypedconst;
|
||
begin
|
||
result:=llvm_getmetadatareftypedconst(def_meta_node(def));
|
||
end;
|
||
|
||
constructor TDebugInfoLLVM.Create;
|
||
begin
|
||
inherited Create;
|
||
|
||
fenums:=nil;
|
||
fretainedtypes:=nil;
|
||
fglobals:=nil;
|
||
femptyexpression:=nil;
|
||
fderefexpression:=nil;
|
||
fcunode:=nil;
|
||
|
||
ffilemeta:=thashset.Create(10000,true,false);
|
||
flocationmeta:=thashset.Create(10000,true,false);
|
||
flexicalblockfilemeta:=thashset.Create(100,true,false);
|
||
fdefmeta:=TLLVMMetaDefHashSet.Create(10000,true,false);
|
||
fstaticvarsymdecl:=thashset.create(10000,true,false);
|
||
|
||
defnumberlist:=TFPObjectList.create(false);
|
||
deftowritelist:=TFPObjectList.create(false);
|
||
|
||
vardatadef:=nil;
|
||
end;
|
||
|
||
|
||
destructor TDebugInfoLLVM.Destroy;
|
||
begin
|
||
// don't free fenums/fretainedtypes/fglobals, they get emitted in the assembler list
|
||
ffilemeta.free;
|
||
ffilemeta:=nil;
|
||
flocationmeta.free;
|
||
flocationmeta:=nil;
|
||
flexicalblockfilemeta.free;
|
||
flexicalblockfilemeta:=nil;
|
||
fdefmeta.free;
|
||
fdefmeta:=nil;
|
||
fstaticvarsymdecl.free;
|
||
fstaticvarsymdecl:=nil;
|
||
flocalvarsymmeta.free;
|
||
flocalvarsymmeta:=nil;
|
||
defnumberlist.free;
|
||
defnumberlist:=nil;
|
||
deftowritelist.free;
|
||
deftowritelist:=nil;
|
||
fcunode.free;
|
||
fcunode:=nil;
|
||
inherited Destroy;
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.ensuremetainit;
|
||
begin
|
||
if not assigned(fllvm_dbg_declare_pd) then
|
||
fllvm_dbg_declare_pd:=search_system_proc('llvm_dbg_declare');
|
||
if not assigned(fllvm_dbg_addr_pd) then
|
||
fllvm_dbg_addr_pd:=search_system_proc('llvm_dbg_addr');
|
||
if not assigned(fenums) then
|
||
begin
|
||
fenums:=tai_llvmunnamedmetadatanode.create;
|
||
fretainedtypes:=tai_llvmunnamedmetadatanode.create;
|
||
fglobals:=tai_llvmunnamedmetadatanode.create;
|
||
femptyexpression:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DIExpression);
|
||
fderefexpression:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DIExpression);
|
||
fderefexpression.addenum('','DW_OP_deref');
|
||
fcunode:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DICompileUnit);
|
||
end;
|
||
end;
|
||
|
||
procedure TDebugInfoLLVM.resetfornewmodule;
|
||
var
|
||
i: longint;
|
||
begin
|
||
{ for LLVM, we need to generate the procdef type info (or at least
|
||
temporary references to it) already during the generation of the line
|
||
info (all line info metadata needs a reference to its parent scope,
|
||
the procdef). Since the line info is generated per procedure and
|
||
the type info only at the end, we can't allocate the type info
|
||
structures at the start of the type info generation like for other
|
||
debug info producers. Instead, we have to initialise everything in the
|
||
constructor, and then reset it at the end of the debug info pass
|
||
(inserting the module info) }
|
||
ffilemeta.Clear;
|
||
flocationmeta.Clear;
|
||
flexicalblockfilemeta.Clear;
|
||
fdefmeta.free;
|
||
fstaticvarsymdecl.Clear;
|
||
{ one item per def, plus some extra space in case of nested types,
|
||
externally used types etc (it will grow further if necessary) }
|
||
i:=current_module.localsymtable.DefList.count*4;
|
||
if assigned(current_module.globalsymtable) then
|
||
inc(i,current_module.globalsymtable.DefList.count*2);
|
||
fdefmeta:=TLLVMMetaDefHashSet.Create(i,true,false);
|
||
|
||
defnumberlist.Clear;
|
||
deftowritelist.Clear;
|
||
fcunode:=nil;
|
||
fenums:=nil;
|
||
fretainedtypes:=nil;
|
||
fglobals:=nil;
|
||
femptyexpression:=nil;
|
||
fderefexpression:=nil;
|
||
end;
|
||
|
||
procedure TDebugInfoLLVM.collectglobalsyms;
|
||
var
|
||
i: TAsmListType;
|
||
hp: tai;
|
||
begin
|
||
for i in globaldataasmlisttypes do
|
||
begin
|
||
if not assigned(current_asmdata.AsmLists[i]) then
|
||
continue;
|
||
hp:=tai(current_asmdata.AsmLists[i].First);
|
||
while assigned(hp) do
|
||
begin
|
||
if (hp.typ=ait_llvmdecl) and
|
||
assigned(taillvmdecl(hp).sym) then
|
||
staticvarsym_set_decl(taillvmdecl(hp).sym,taillvmdecl(hp));
|
||
hp:=tai(hp.next);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.updatelocalvardbginfo(hp: taillvm; pd: tprocdef; functionscope: tai_llvmspecialisedmetadatanode);
|
||
var
|
||
opindex, callparaindex: longint;
|
||
paras: tfplist;
|
||
sympara,
|
||
exprpara: pllvmcallpara;
|
||
sym: tabstractnormalvarsym;
|
||
dilocalvar: tai_llvmspecialisedmetadatanode;
|
||
isnewlocalvardi,
|
||
deref: boolean;
|
||
begin
|
||
{ not really clean since hardcoding the structure of the call
|
||
instruction's procdef encoding, but quick }
|
||
if (hp.oper[taillvm.callpdopernr]^.def.typ<>pointerdef) or
|
||
((tpointerdef(hp.oper[taillvm.callpdopernr]^.def).pointeddef<>fllvm_dbg_declare_pd) and
|
||
(tpointerdef(hp.oper[taillvm.callpdopernr]^.def).pointeddef<>fllvm_dbg_addr_pd)) then
|
||
exit;
|
||
deref:=false;
|
||
|
||
sympara:=hp.getcallpara(1);
|
||
exprpara:=hp.getcallpara(2);
|
||
|
||
if sympara^.val.typ<>top_local then
|
||
internalerror(2022052613);
|
||
sym:=tabstractnormalvarsym(sympara^.val.localsym);
|
||
dilocalvar:=localvarsym_get_meta(sym,isnewlocalvardi);
|
||
sympara^.loadtai(llvm_getmetadatareftypedconst(dilocalvar));
|
||
if isnewlocalvardi then
|
||
begin
|
||
dilocalvar.addstring('name',symname(sym,false));
|
||
if sym.typ=paravarsym then
|
||
begin
|
||
dilocalvar.addint64('arg',tparavarsym(sym).paranr);
|
||
if paramanager.push_addr_param(sym.varspez,sym.vardef,pd.proccalloption) then
|
||
deref:=true;
|
||
end;
|
||
dilocalvar.addmetadatarefto('scope',functionscope);
|
||
try_add_file_metaref(dilocalvar,sym.fileinfo,false);
|
||
dilocalvar.addmetadatarefto('type',def_meta_node(sym.vardef));
|
||
if vo_is_self in sym.varoptions then
|
||
dilocalvar.addenum('flags','DIFlagArtificial');
|
||
end
|
||
else
|
||
begin
|
||
if (sym.typ=paravarsym) and
|
||
paramanager.push_addr_param(sym.varspez,sym.vardef,pd.proccalloption) then
|
||
deref:=true;
|
||
end;
|
||
|
||
if not deref then
|
||
exprpara^.loadtai(llvm_getmetadatareftypedconst(femptyexpression))
|
||
else
|
||
exprpara^.loadtai(llvm_getmetadatareftypedconst(fderefexpression));
|
||
end;
|
||
|
||
|
||
function TDebugInfoLLVM.file_getmetanode(moduleindex: tfileposmoduleindex; fileindex: tfileposfileindex): tai_llvmspecialisedmetadatanode;
|
||
var
|
||
infile: tinputfile;
|
||
dirname: TSymStr;
|
||
item: PHashSetItem;
|
||
metaitem: tai_llvmspecialisedmetadatanode;
|
||
modfileindex: packed record
|
||
moduleindex: tfileposmoduleindex;
|
||
fileindex: tfileposfileindex;
|
||
end;
|
||
begin
|
||
modfileindex.moduleindex:=moduleindex;
|
||
modfileindex.fileindex:=fileindex;
|
||
item:=ffilemeta.FindOrAdd(@modfileindex,sizeof(modfileindex));
|
||
if not assigned(item^.Data) then
|
||
begin
|
||
infile:=get_module(moduleindex).sourcefiles.get_file(fileindex);
|
||
if not assigned(infile) then
|
||
begin
|
||
result:=nil;
|
||
exit;
|
||
end;
|
||
if infile.path = '' then
|
||
dirname:=absolute_llvm_path('.')
|
||
else
|
||
begin
|
||
{ add the canonical form here already to avoid problems with }
|
||
{ paths such as './' etc }
|
||
dirname:=absolute_llvm_path(infile.path);
|
||
end;
|
||
if dirname='' then
|
||
dirname:='.';
|
||
metaitem:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DIFile);
|
||
metaitem.addstring('filename',infile.name);
|
||
metaitem.addstring('directory',dirname);
|
||
current_asmdata.AsmLists[al_dwarf_line].concat(metaitem);
|
||
item^.Data:=metaitem;
|
||
end;
|
||
result:=tai_llvmspecialisedmetadatanode(item^.Data);
|
||
end;
|
||
|
||
|
||
function TDebugInfoLLVM.filepos_getmetanode(const filepos: tfileposinfo; const functionfileinfo: tfileposinfo; const functionscope: tai_llvmspecialisedmetadatanode; nolineinfo: boolean): tai_llvmspecialisedmetadatanode;
|
||
var
|
||
item: PHashSetItem;
|
||
filemeta,
|
||
locationscopemeta: tai_llvmspecialisedmetadatanode;
|
||
lexicalblockkey: packed record
|
||
scopemeta,
|
||
filemeta: tai_llvmspecialisedmetadatanode;
|
||
end;
|
||
locationkey: packed record
|
||
scope: tai_llvmspecialisedmetadatanode;
|
||
line: tfileposline;
|
||
column: tfileposcolumn;
|
||
end;
|
||
|
||
begin
|
||
result:=nil;
|
||
if (filepos.fileindex<>0) then
|
||
filemeta:=file_getmetanode(filepos.moduleindex,filepos.fileindex)
|
||
else
|
||
filemeta:=file_getmetanode(functionfileinfo.moduleindex,functionfileinfo.fileindex);
|
||
if not assigned(filemeta) then
|
||
exit;
|
||
if (filepos.fileindex<>0) and
|
||
(filepos.fileindex<>functionfileinfo.fileindex) then
|
||
begin
|
||
lexicalblockkey.scopemeta:=functionscope;
|
||
lexicalblockkey.filemeta:=filemeta;
|
||
item:=flexicalblockfilemeta.FindOrAdd(@lexicalblockkey,sizeof(lexicalblockkey));
|
||
if not assigned(item^.Data) then
|
||
begin
|
||
locationscopemeta:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DILexicalBlockFile);
|
||
locationscopemeta.addmetadatarefto('scope',functionscope);
|
||
locationscopemeta.addmetadatarefto('file',filemeta);
|
||
locationscopemeta.addint64('discriminator',0);
|
||
current_asmdata.AsmLists[al_dwarf_line].concat(locationscopemeta);
|
||
item^.Data:=locationscopemeta;
|
||
end
|
||
else
|
||
locationscopemeta:=tai_llvmspecialisedmetadatanode(item^.Data);
|
||
end
|
||
else
|
||
locationscopemeta:=functionscope;
|
||
locationkey.scope:=locationscopemeta;
|
||
if not nolineinfo then
|
||
begin
|
||
locationkey.line:=filepos.line;
|
||
locationkey.column:=filepos.column;
|
||
end
|
||
else
|
||
begin
|
||
locationkey.line:=0;
|
||
locationkey.column:=0;
|
||
end;
|
||
item:=flocationmeta.FindOrAdd(@locationkey,sizeof(locationkey));
|
||
if not assigned(item^.Data) then
|
||
begin
|
||
result:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DILocation);
|
||
if not nolineinfo then
|
||
begin
|
||
result.addqword('line',filepos.line);
|
||
result.addqword('column',filepos.column);
|
||
end
|
||
else
|
||
result.addqword('line',0);
|
||
result.addmetadatarefto('scope',locationscopemeta);
|
||
current_asmdata.AsmLists[al_dwarf_line].concat(result);
|
||
item^.Data:=result;
|
||
end
|
||
else
|
||
result:=tai_llvmspecialisedmetadatanode(item^.Data);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.try_add_file_metaref(dinode: tai_llvmspecialisedmetadatanode; const fileinfo: tfileposinfo; includescope: boolean);
|
||
var
|
||
filemeta: tai_llvmbasemetadatanode;
|
||
begin
|
||
filemeta:=file_getmetanode(fileinfo.moduleindex,fileinfo.fileindex);
|
||
if assigned(filemeta) then
|
||
begin
|
||
if includescope then
|
||
begin
|
||
dinode.addmetadatarefto('scope',filemeta);
|
||
end;
|
||
dinode.addmetadatarefto('file',filemeta);
|
||
dinode.addqword('line',fileinfo.line);
|
||
end;
|
||
end;
|
||
|
||
|
||
function TDebugInfoLLVM.add_line_metanode(const fileinfo: tfileposinfo): tai_llvmspecialisedmetadatanode;
|
||
var
|
||
filemeta: tai_llvmbasemetadatanode;
|
||
begin
|
||
filemeta:=file_getmetanode(fileinfo.moduleindex,fileinfo.fileindex);
|
||
if not assigned(filemeta) then
|
||
internalerror(2022041701);
|
||
result:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DILocation);
|
||
result.addqword('line',fileinfo.line);
|
||
result.addqword('column',fileinfo.column);
|
||
result.addmetadatarefto('scope',filemeta);
|
||
current_asmdata.AsmLists[al_dwarf_line].concat(result);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appenddef_ord(list:TAsmList;def:torddef);
|
||
var
|
||
ordtype: tordtype;
|
||
dinode: tai_llvmspecialisedmetadatanode;
|
||
begin
|
||
{ nothing, must be referenced as "null" in the using declaration }
|
||
if is_void(def) then
|
||
exit;
|
||
|
||
ordtype:=def.ordtype;
|
||
if ordtype=customint then
|
||
ordtype:=range_to_basetype(def.low,def.high);
|
||
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DIBasicType);
|
||
case ordtype of
|
||
s8bit,
|
||
s16bit,
|
||
s32bit,
|
||
u8bit,
|
||
u16bit,
|
||
u32bit,
|
||
u64bit,
|
||
s64bit,
|
||
u128bit,
|
||
s128bit:
|
||
begin
|
||
dinode.addqword('size',def.size*8);
|
||
if def.alignment<>def.size then
|
||
dinode.addqword('align',def.alignment*8);
|
||
{ generate proper signed/unsigned info for types like 0..3 }
|
||
{ these are s8bit, but should be identified as unsigned }
|
||
{ because otherwise they are interpreted wrongly when used }
|
||
{ in a bitpacked record }
|
||
if def.low<0 then
|
||
dinode.addenum('encoding','DW_ATE_signed')
|
||
else
|
||
dinode.addenum('encoding','DW_ATE_unsigned');
|
||
end;
|
||
uvoid :
|
||
begin
|
||
{ checked above }
|
||
end;
|
||
uchar,
|
||
uwidechar :
|
||
begin
|
||
dinode.addqword('size',def.size*8);
|
||
dinode.addenum('encoding','DW_ATE_unsigned_char');
|
||
end;
|
||
pasbool1,
|
||
pasbool8,
|
||
bool8bit,
|
||
pasbool16,
|
||
bool16bit,
|
||
pasbool32,
|
||
bool32bit,
|
||
pasbool64,
|
||
bool64bit:
|
||
begin
|
||
dinode.addqword('size',def.size*8);
|
||
dinode.addenum('encoding','DW_ATE_boolean');
|
||
end;
|
||
scurrency:
|
||
begin
|
||
{ we should use DW_ATE_signed_fixed, however it isn't supported yet by LLVM }
|
||
dinode.addqword('size',def.size*8);
|
||
dinode.addenum('encoding','DW_ATE_signed');
|
||
end;
|
||
customint:
|
||
internalerror(2021111502);
|
||
end;
|
||
list.concat(dinode);
|
||
end;
|
||
|
||
procedure TDebugInfoLLVM.appenddef_float(list:TAsmList;def:tfloatdef);
|
||
var
|
||
dinode: tai_llvmspecialisedmetadatanode;
|
||
begin
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DIBasicType);
|
||
case def.floattype of
|
||
s32real,
|
||
s64real,
|
||
s80real,
|
||
sc80real,
|
||
s128real:
|
||
begin
|
||
dinode.addqword('size',def.size*8);
|
||
if def.alignment<>def.size then
|
||
dinode.addqword('align',def.alignment*8);
|
||
dinode.addenum('encoding','DW_ATE_float');
|
||
end;
|
||
s64currency:
|
||
begin
|
||
{ we should use DW_ATE_signed_fixed, however it isn't supported yet by LLVM }
|
||
dinode.addqword('size',def.size*8);
|
||
dinode.addenum('encoding','DW_ATE_signed');
|
||
end;
|
||
s64comp:
|
||
begin
|
||
{ we should use DW_ATE_signed_fixed, however it isn't supported yet by LLVM }
|
||
dinode.addqword('size',def.size*8);
|
||
dinode.addenum('encoding','DW_ATE_signed');
|
||
end;
|
||
end;
|
||
list.concat(dinode);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appenddef_enum(list:TAsmList;def:tenumdef);
|
||
var
|
||
hp : tenumsym;
|
||
i : longint;
|
||
dinode: tai_llvmspecialisedmetadatanode;
|
||
enumelem: tai_llvmspecialisedmetadatanode;
|
||
enumlist: tai_llvmunnamedmetadatanode;
|
||
begin
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DICompositeType);
|
||
dinode.addenum('tag','DW_TAG_enumeration_type');
|
||
dinode.addqword('size',def.size*8);
|
||
dinode.addstring('identifier',def.mangledparaname);
|
||
|
||
{ register in module's list of enums (to ensure the debug info gets
|
||
emitted even if the enum is not used in the current module) }
|
||
fenums.addvalue(llvm_getmetadatareftypedconst(dinode));
|
||
|
||
enumlist:=tai_llvmunnamedmetadatanode.create;
|
||
{ add enum symbols }
|
||
for i:=0 to def.symtable.SymList.Count-1 do
|
||
begin
|
||
hp:=tenumsym(def.symtable.SymList[i]);
|
||
if hp.value<def.minval then
|
||
continue
|
||
else if hp.value>def.maxval then
|
||
break;
|
||
enumelem:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DIEnumerator);
|
||
enumelem.addstring('name',symname(hp, false));
|
||
enumelem.addint64('value',hp.value);
|
||
list.concat(enumelem);
|
||
enumlist.addvalue(llvm_getmetadatareftypedconst(enumelem));
|
||
end;
|
||
if enumlist.valuecount<>0 then
|
||
begin
|
||
list.concat(enumlist);
|
||
dinode.addmetadatarefto('elements',enumlist);
|
||
end
|
||
else
|
||
begin
|
||
enumlist.free;
|
||
end;
|
||
list.concat(dinode);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appenddef_array(list:TAsmList;def:tarraydef);
|
||
var
|
||
dinode,
|
||
subrangenode,
|
||
exprnode: tai_llvmspecialisedmetadatanode;
|
||
arrayrangenode: tai_llvmunnamedmetadatanode;
|
||
size : qword;
|
||
nesteddef: tdef;
|
||
power: longint;
|
||
flags: TLLVMDIFlags;
|
||
begin
|
||
if is_dynamic_array(def) { and
|
||
not(llvmflag_array_datalocation in llvmversion_properties[current_settings.llvmversion]) } then
|
||
begin
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DIDerivedType);
|
||
dinode.addenum('tag','DW_TAG_pointer_type');
|
||
dinode.addmetadatarefto('baseType',def_meta_node(def.elementdef));
|
||
dinode.addqword('size',def.size*8);
|
||
list.concat(dinode);
|
||
exit;
|
||
end;
|
||
|
||
{ open arrays etc need to access the high parameter to define their range,
|
||
which is not possible here since we need the parasym rather than the def }
|
||
if is_open_array(def) then
|
||
begin
|
||
(*
|
||
if llvmflag_array_datalocation in llvmversion_properties[current_settings.llvmversion] then
|
||
begin
|
||
dinode:=def_meta_impl(def);
|
||
{ should be generated as part of the parasym }
|
||
if not assigned(dinode) then
|
||
internalerror(2021112002);
|
||
end
|
||
else *)
|
||
begin
|
||
{ no idea about the size, generate an array of 1 element -- although it could be empty }
|
||
appenddef_array_internal(list,def,def.elementdef,0,1);
|
||
end;
|
||
exit;
|
||
end;
|
||
|
||
if is_array_of_const(def) then
|
||
begin
|
||
{ no idea about the size, generate an array of 1 element -- although it could be empty }
|
||
appenddef_array_internal(list,def,def.elementdef,0,1);
|
||
exit;
|
||
end;
|
||
|
||
if is_special_array(def)
|
||
and not((llvmflag_array_datalocation in llvmversion_properties[current_settings.llvmversion]) and
|
||
is_dynamic_array(def)) then
|
||
internalerror(2021121902);
|
||
|
||
{ todo: proper support for bitpacked arrays }
|
||
if is_packed_array(def) and
|
||
(((def.elementdef.packedbitsize mod 8)<>0) or
|
||
not ispowerof2(def.elementdef.packedbitsize div 8,power)) then
|
||
begin
|
||
{ for now just encode as an array of bytes }
|
||
appenddef_array_internal(list,def,u8inttype,0,def.size-1);
|
||
exit;
|
||
end;
|
||
|
||
{ collection of all ranges of the array (to support multi-dimensional arrays) }
|
||
arrayrangenode:=tai_llvmunnamedmetadatanode.create;
|
||
list.concat(arrayrangenode);
|
||
|
||
{ range of the array }
|
||
subrangenode:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DISubrange);
|
||
if is_dynamic_array(def) then
|
||
begin
|
||
exprnode:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DIExpression);
|
||
exprnode.addenum('','DW_OP_push_object_address');
|
||
exprnode.addenum('','DW_OP_constu');
|
||
exprnode.addint64('',ord(sizeof(pint)));
|
||
exprnode.addenum('','DW_OP_minus');
|
||
exprnode.addenum('','DW_OP_deref');
|
||
list.concat(exprnode);
|
||
subrangenode.addmetadatarefto('upperBound',exprnode);
|
||
subrangenode.addint64('lowerBound',def.lowrange);
|
||
end
|
||
else
|
||
begin
|
||
subrangenode.addqword('count',def.highrange-def.lowrange+1);
|
||
subrangenode.addint64('lowerBound',def.lowrange);
|
||
end;
|
||
list.concat(subrangenode);
|
||
nesteddef:=def.elementdef;
|
||
arrayrangenode.addvalue(llvm_getmetadatareftypedconst(subrangenode));
|
||
while (nesteddef.typ=arraydef) and
|
||
not is_special_array(nesteddef) do
|
||
begin
|
||
subrangenode:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DISubrange);
|
||
subrangenode.addqword('count',tarraydef(nesteddef).highrange-tarraydef(nesteddef).lowrange+1);
|
||
subrangenode.addint64('lowerBound',tarraydef(nesteddef).lowrange);
|
||
list.concat(subrangenode);
|
||
arrayrangenode.addvalue(llvm_getmetadatareftypedconst(subrangenode));
|
||
nesteddef:=tarraydef(nesteddef).elementdef;
|
||
end;
|
||
{ the array definition }
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DICompositeType);
|
||
dinode.addenum('tag','DW_TAG_array_type');
|
||
dinode.addmetadatarefto('baseType',def_meta_node(nesteddef));
|
||
dinode.addmetadatarefto('elements',arrayrangenode);
|
||
if is_vector(def) then
|
||
dinode.addenum('flags','DIFlagVector');
|
||
if not is_dynamic_array(def) then
|
||
{$ifdef cpu64bitalu}
|
||
if def.size>=(qword(1) shl 61) then
|
||
{ 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) - 1)*8)
|
||
else
|
||
{$endif def cpu64bitalu}
|
||
dinode.addqword('size',def.size*8)
|
||
else
|
||
begin
|
||
exprnode:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DIExpression);
|
||
exprnode.addenum('','DW_OP_LLVM_implicit_pointer');
|
||
list.concat(exprnode);
|
||
dinode.addmetadatarefto('dataLocation',exprnode);
|
||
end;
|
||
list.concat(dinode);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appenddef_record(list:TAsmList;def:trecorddef);
|
||
begin
|
||
if assigned(def.objname) then
|
||
appenddef_record_named(list,def,def,def.objname^)
|
||
else
|
||
appenddef_record_named(list,def,def,'');
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appenddef_record_named(list:TAsmList; fordef: tdef; def:trecorddef; const name: TSymStr);
|
||
var
|
||
dinode: tai_llvmspecialisedmetadatanode;
|
||
begin
|
||
dinode:=def_set_meta_impl(fordef,tspecialisedmetadatanodekind.DICompositeType);
|
||
list.concat(dinode);
|
||
dinode.addenum('tag','DW_TAG_structure_type');
|
||
appenddef_struct_named(list,def,dinode,tai_llvmunnamedmetadatanode.create,name);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appenddef_struct_named(list: TAsmList; def: tabstractrecorddef; structdi: tai_llvmspecialisedmetadatanode; initialfieldlist: tai_llvmunnamedmetadatanode; const name: TSymStr);
|
||
var
|
||
cappedsize: asizeuint;
|
||
begin
|
||
if (name<>'') then
|
||
structdi.addstring('name',name);
|
||
if assigned(def.typesym) then
|
||
try_add_file_metaref(structdi,def.typesym.fileinfo,false);
|
||
if is_packed_record_or_object(def) then
|
||
cappedsize:=tabstractrecordsymtable(def.symtable).datasize
|
||
{$ifdef cpu64bitalu}
|
||
else if def.size>=(qword(1) shl 61) then
|
||
{ 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 }
|
||
cappedsize:=((qword(1) shl 61) - 1)*8
|
||
{$endif def cpu64bitalu}
|
||
else
|
||
cappedsize:=tabstractrecordsymtable(def.symtable).datasize*8;
|
||
structdi.addqword('size',cappedsize);
|
||
|
||
appenddef_struct_fields(list,def,structdi,initialfieldlist,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: tabstractrecordsymtable;
|
||
scope,
|
||
fielddi,
|
||
uniondi,
|
||
structdi: tai_llvmspecialisedmetadatanode;
|
||
fieldlist: tai_llvmunnamedmetadatanode;
|
||
i, varindex: longint;
|
||
field: tfieldvarsym;
|
||
bitoffset: asizeuint;
|
||
bpackedrecst,
|
||
classorobject: boolean;
|
||
begin
|
||
recst:=tabstractrecordsymtable(def.symtable);
|
||
bpackedrecst:=recst.fieldalignment=bit_alignment;
|
||
scope:=defdinode;
|
||
variantinfolist:=nil;
|
||
classorobject:=is_class_or_interface_or_object(def);
|
||
|
||
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]);
|
||
if (sp_static in field.symoptions) then
|
||
exit;
|
||
|
||
{ 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);
|
||
|
||
fielddi:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DIDerivedType);
|
||
fielddi.addenum('tag','DW_TAG_member');
|
||
fielddi.addmetadatarefto('scope',scope);
|
||
try_add_file_metaref(fielddi,field.fileinfo,false);
|
||
fielddi.addmetadatarefto('baseType',uniondi);
|
||
fielddi.addint64('size',cappedsize-min(field.bitoffset,cappedsize));
|
||
bitoffset:=bitoffsetfromvariantstart(field,variantinfolist,cappedsize);
|
||
if bitoffset<>0 then
|
||
fielddi.addqword('offset',bitoffset);
|
||
list.concat(fielddi);
|
||
fieldlist.addvalue(llvm_getmetadatareftypedconst(fielddi));
|
||
|
||
list.concat(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);
|
||
{ the vmt field's type is voidpointerdef, because when it gets
|
||
inserted we can't build the vmt's def yet }
|
||
if classorobject and
|
||
(field=tobjectdef(def).vmt_field) then
|
||
fielddi.addmetadatarefto('baseType',def_meta_node(cpointerdef.getreusable(tobjectdef(def).vmt_def)))
|
||
else
|
||
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);
|
||
{ currently only vmt }
|
||
if field.visibility=vis_hidden then
|
||
fielddi.addenum('flags','DIFlagArtificial');
|
||
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;
|
||
begin
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DIDerivedType);
|
||
dinode.addenum('tag','DW_TAG_pointer_type');
|
||
if not(is_voidpointer(def)) then
|
||
dinode.addmetadatarefto('baseType',def_meta_node(def.pointeddef))
|
||
else
|
||
dinode.addmetadatarefto('baseType',nil);
|
||
list.concat(dinode);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appenddef_formal(list: TAsmList; def: tformaldef);
|
||
var
|
||
dinode: tai_llvmspecialisedmetadatanode;
|
||
begin
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DIDerivedType);
|
||
dinode.addenum('tag','DW_TAG_pointer_type');
|
||
dinode.addmetadatarefto('baseType',nil);
|
||
list.concat(dinode);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appenddef_string(list:TAsmList;def:tstringdef);
|
||
|
||
procedure addnormalstringdef(const name: TSymStr; lendef: tdef; maxlen: asizeuint);
|
||
var
|
||
dinode,
|
||
subrangenode,
|
||
exprnode: tai_llvmspecialisedmetadatanode;
|
||
arrayrangenode: tai_aggregatetypedconst;
|
||
{ maxlen can be > high(int64) }
|
||
slen : asizeuint;
|
||
arr : tasmlabel;
|
||
begin
|
||
{ fix length of openshortstring }
|
||
slen:=aword(def.len);
|
||
if (slen=0) or
|
||
(slen>maxlen) then
|
||
slen:=maxlen;
|
||
appenddef_array_internal(list,def,cansichartype,0,slen);
|
||
end;
|
||
|
||
var
|
||
dinode: tai_llvmspecialisedmetadatanode;
|
||
begin
|
||
case def.stringtype of
|
||
st_shortstring:
|
||
begin
|
||
addnormalstringdef('ShortString',u8inttype,255);
|
||
end;
|
||
st_longstring:
|
||
begin
|
||
{ a) we don't actually support variables of this type currently
|
||
b) this type is only used as the type for constant strings
|
||
> 255 characters
|
||
c) in such a case, gdb will allocate and initialise enough
|
||
memory to hold the maximum size for such a string
|
||
-> don't use high(qword)/high(cardinal) as maximum, since that
|
||
will cause exhausting the VM space, but some "reasonably high"
|
||
number that should be enough for most constant strings
|
||
}
|
||
{$ifdef cpu64bitaddr}
|
||
addnormalstringdef('LongString',u64inttype,qword(1024*1024));
|
||
{$endif cpu64bitaddr}
|
||
{$ifdef cpu32bitaddr}
|
||
addnormalstringdef('LongString',u32inttype,cardinal(1024*1024));
|
||
{$endif cpu32bitaddr}
|
||
{$ifdef cpu16bitaddr}
|
||
addnormalstringdef('LongString',u16inttype,cardinal(1024));
|
||
{$endif cpu16bitaddr}
|
||
end;
|
||
st_ansistring:
|
||
begin
|
||
// Todo: dynamic length "array"
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DIDerivedType);
|
||
dinode.addenum('tag','DW_TAG_pointer_type');
|
||
dinode.addmetadatarefto('baseType',def_meta_node(cansichartype));
|
||
list.concat(dinode);
|
||
end;
|
||
st_unicodestring,
|
||
st_widestring:
|
||
begin
|
||
// Todo: dynamic length "array"
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DIDerivedType);
|
||
dinode.addenum('tag','DW_TAG_pointer_type');
|
||
dinode.addmetadatarefto('baseType',def_meta_node(cwidechartype));
|
||
list.concat(dinode);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TDebugInfoLLVM.appenddef_procvar(list:TAsmList;def:tprocvardef);
|
||
var
|
||
dinode: tai_llvmspecialisedmetadatanode;
|
||
begin
|
||
{ plain pointer for now }
|
||
if def.is_addressonly then
|
||
begin
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DIDerivedType);
|
||
dinode.addenum('tag','DW_TAG_pointer_type');
|
||
dinode.addmetadatarefto('baseType',nil);
|
||
list.concat(dinode);
|
||
end
|
||
else
|
||
begin
|
||
appenddef_array_internal(list,def,voidcodepointertype,1,2);
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appenddef_file(list: TAsmList; def: tfiledef);
|
||
var
|
||
dinode: tai_llvmspecialisedmetadatanode;
|
||
begin
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DICompositeType);
|
||
dinode.addenum('tag','DW_TAG_structure_type');
|
||
if assigned(def.typesym) then
|
||
dinode.addstring('name',symname(def.typesym, false));
|
||
dinode.addqword('size',def.size*8);
|
||
|
||
list.concat(dinode);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appenddef_object(list: TAsmList; def: tobjectdef);
|
||
var
|
||
dinode,
|
||
structdi,
|
||
inheritancedi: tai_llvmspecialisedmetadatanode;
|
||
fields: tai_llvmunnamedmetadatanode;
|
||
begin
|
||
inheritancedi:=nil;
|
||
fields:=tai_llvmunnamedmetadatanode.create;
|
||
if assigned(def.childof) then
|
||
begin
|
||
inheritancedi:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DIDerivedType);
|
||
list.concat(inheritancedi);
|
||
inheritancedi.addenum('tag','DW_TAG_inheritance');
|
||
if is_implicit_pointer_object_type(def) then
|
||
inheritancedi.addmetadatarefto('baseType',def_meta_class_struct(def.childof))
|
||
else
|
||
inheritancedi.addmetadatarefto('baseType',def_meta_node(def.childof));
|
||
{ Pascal only has public inheritance }
|
||
if def.objecttype<>odt_cppclass then
|
||
inheritancedi.addenum('flags','DIFlagPublic');
|
||
fields.addvalue(llvm_getmetadatareftypedconst(inheritancedi));
|
||
end;
|
||
if is_implicit_pointer_object_type(def) then
|
||
begin
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DIDerivedType);
|
||
dinode.addenum('tag','DW_TAG_pointer_type');
|
||
|
||
structdi:=def_meta_class_struct(def);
|
||
list.concat(structdi);
|
||
structdi.addenum('tag','DW_TAG_class_type');
|
||
appenddef_struct_named(list,def,structdi,fields,def.objname^);
|
||
|
||
{ implicit pointer }
|
||
dinode.addmetadatarefto('baseType',structdi);
|
||
end
|
||
else case def.objecttype of
|
||
odt_cppclass,
|
||
odt_object:
|
||
begin
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DICompositeType);
|
||
dinode.addenum('tag','DW_TAG_class_type');
|
||
appenddef_struct_named(list,def,dinode,fields,def.objname^);
|
||
end;
|
||
odt_objcclass:
|
||
begin
|
||
{ Objective-C class: same as regular class, except for
|
||
a) Apple-specific tag that identifies it as an Objective-C class
|
||
b) use extname^ instead of objname
|
||
}
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DICompositeType);
|
||
dinode.addenum('tag','DW_TAG_class_type');
|
||
dinode.addenum('runtimeLang','DW_LANG_ObjC');
|
||
appenddef_struct_named(list,def,dinode,fields,def.objextname^);
|
||
end;
|
||
odt_objcprotocol:
|
||
begin
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DIDerivedType);
|
||
dinode.addenum('tag','DW_TAG_pointer_type');
|
||
dinode.addmetadatarefto('baseType',nil);
|
||
end;
|
||
else
|
||
internalerror(2022060710);
|
||
end;
|
||
list.concat(dinode);
|
||
if assigned(inheritancedi) then
|
||
inheritancedi.addmetadatarefto('scope',dinode);
|
||
write_symtable_procdefs(current_asmdata.asmlists[al_dwarf_info],def.symtable);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appenddef_set(list: TAsmList; def: tsetdef);
|
||
begin
|
||
appenddef_array_internal(list,def,u8inttype,0,def.size-1);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appenddef_undefined(list: TAsmList; def: tundefineddef);
|
||
var
|
||
dinode: tai_llvmspecialisedmetadatanode;
|
||
begin
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DIDerivedType);
|
||
dinode.addenum('tag','DW_TAG_pointer_type');
|
||
dinode.addmetadatarefto('baseType',nil);
|
||
list.concat(dinode);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appenddef_classref(list: TAsmList; def: tclassrefdef);
|
||
var
|
||
dinode: tai_llvmspecialisedmetadatanode;
|
||
begin
|
||
dinode:=def_set_meta_impl(def,tspecialisedmetadatanodekind.DIDerivedType);
|
||
dinode.addenum('tag','DW_TAG_pointer_type');
|
||
dinode.addmetadatarefto('baseType',nil);
|
||
list.concat(dinode);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appenddef_variant(list: TAsmList; def: tvariantdef);
|
||
begin
|
||
if assigned(vardatadef) then
|
||
appenddef_record_named(list,def,trecorddef(vardatadef),'Variant');
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.afterappenddef(list:TAsmList;def:tdef);
|
||
var
|
||
tempdinode,
|
||
refdinode,
|
||
impldinode: tai_llvmspecialisedmetadatanode;
|
||
begin
|
||
if def.typ=procdef then
|
||
exit;
|
||
|
||
if is_void(def) then
|
||
exit;
|
||
|
||
refdinode:=def_meta_node(def);
|
||
impldinode:=def_meta_impl(def);
|
||
if not assigned(impldinode) then
|
||
internalerror(2021120501);
|
||
|
||
if is_objc_class_or_protocol(def) then
|
||
begin
|
||
{ for Objective-C classes, the named typedef must refer to the
|
||
struct itself, not to the pointer of the struct; Objective-C
|
||
classes are not implicit pointers in Objective-C itself, only
|
||
in FPC. So make the def label point to a pointer to the
|
||
typedef, which in turn refers to the actual struct (for Delphi-
|
||
style classes, the def points to the typedef, which refers to
|
||
a pointer to the actual struct) }
|
||
|
||
{ implicit pointer }
|
||
tempdinode:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DIDerivedType);
|
||
refdinode.addenum('tag','DW_TAG_pointer_type');
|
||
refdinode.addmetadatarefto('baseType',tempdinode);
|
||
list.concat(refdinode);
|
||
{ typedef }
|
||
refdinode:=tempdinode;
|
||
end;
|
||
|
||
if assigned(def.typesym) and
|
||
not(df_generic in def.defoptions) then
|
||
begin
|
||
if refdinode=impldinode then
|
||
internalerror(2022110710);
|
||
refdinode.addenum('tag','DW_TAG_typedef');
|
||
refdinode.addstring('name',symname(def.typesym,false));
|
||
try_add_file_metaref(refdinode,def.typesym.fileinfo,false);
|
||
refdinode.addmetadatarefto('baseType',impldinode);
|
||
list.concat(refdinode);
|
||
end
|
||
else if impldinode<>refdinode then
|
||
internalerror(2022110711);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appendprocdef(list:TAsmList; def:tprocdef);
|
||
|
||
procedure adddispflags(dinode: tai_llvmspecialisedmetadatanode; is_definition, is_virtual: boolean);
|
||
var
|
||
dispflags: TSymStr;
|
||
islocal: boolean;
|
||
begin
|
||
islocal:=
|
||
not((po_global in def.procoptions) and
|
||
(def.parast.symtablelevel<=normal_function_level));
|
||
adddefinitionlocal(dinode,is_definition,islocal,not(llvmflag_NoDISPFlags in llvmversion_properties[current_settings.llvmversion]),dispflags);
|
||
if llvmflag_NoDISPFlags in llvmversion_properties[current_settings.llvmversion] then
|
||
begin
|
||
if is_virtual then
|
||
begin
|
||
if not(po_abstractmethod in def.procoptions) then
|
||
dinode.addenum('virtuality','DW_VIRTUALITY_virtual')
|
||
else
|
||
dinode.addenum('virtuality','DW_VIRTUALITY_pure_virtual');
|
||
end;
|
||
exit;
|
||
end;
|
||
|
||
if is_virtual then
|
||
begin
|
||
if dispflags<>'' then
|
||
dispflags:=dispflags+'|';
|
||
if not(po_abstractmethod in def.procoptions) then
|
||
dispflags:=dispflags+'DISPFlagVirtual'
|
||
else
|
||
dispflags:=dispflags+'DISPFlagPureVirtual';
|
||
end
|
||
else
|
||
begin
|
||
{ this one will always be a definition, so no need to check
|
||
whether result is empty }
|
||
if not(llvmflag_NoDISPFlagMainSubprogram in llvmversion_properties[current_settings.llvmversion]) and
|
||
(def.proctypeoption=potype_proginit) then
|
||
dispflags:=dispflags+'|DISPFlagMainSubprogram';
|
||
end;
|
||
if dispflags<>'' then
|
||
dinode.addenum('spFlags',dispflags);
|
||
end;
|
||
|
||
procedure adddiflags(dinode: tai_llvmspecialisedmetadatanode; is_definition: boolean);
|
||
var
|
||
diflags: TSymStr;
|
||
begin
|
||
if (llvmflag_NoDISPFlagMainSubprogram in llvmversion_properties[current_settings.llvmversion]) and
|
||
(def.proctypeoption=potype_proginit) then
|
||
diflags:='DIFlagMainSubprogram'
|
||
else if def.owner.symtabletype in [objectsymtable,recordsymtable] then
|
||
diflags:=visibilitydiflag(def.visibility)
|
||
else
|
||
diflags:='';
|
||
if diflags<>'' then
|
||
dinode.addenum('flags',diflags);
|
||
end;
|
||
|
||
var
|
||
dinode,
|
||
ditypenode : tai_llvmspecialisedmetadatanode;
|
||
fileref : tai_simpletypedconst;
|
||
procdeftai : tai;
|
||
st : tsymtable;
|
||
vmtoffset : pint;
|
||
flags : TSymStr;
|
||
in_currentunit,
|
||
is_virtual : boolean;
|
||
|
||
begin
|
||
{ only write debug info for procedures defined in the current module,
|
||
except in case of methods (clang-compatible)
|
||
}
|
||
in_currentunit:=def.in_currentunit;
|
||
|
||
if not in_currentunit and
|
||
not (def.owner.symtabletype in [objectsymtable,recordsymtable]) then
|
||
exit;
|
||
|
||
{ happens for init procdef of units without init section }
|
||
if in_currentunit and
|
||
not assigned(def.procstarttai) then
|
||
exit;
|
||
|
||
{ These don't contain a taillvmdecl, they are completely generated
|
||
in native assembly. If we want to add debug information to these,
|
||
we have to do it using the regular debug info generation }
|
||
if po_assembler in def.procoptions then
|
||
exit;
|
||
|
||
if df_generic in def.defoptions then
|
||
exit;
|
||
|
||
{ Procdefs are not handled by the regular def writing code, so
|
||
dbg_state is not set/checked for them. Do it here. }
|
||
if (def.dbg_state in [dbg_state_writing,dbg_state_written]) then
|
||
exit;
|
||
defnumberlist.Add(def);
|
||
|
||
def.dbg_state:=dbg_state_writing;
|
||
{ difference compared to other kinds of defs: the DISubProgram gets
|
||
created directly in get_def_metatai because a typedef for a
|
||
DISubProgram does not make sense and is not supported by LLVM ->
|
||
don't set the implementation of the metadata def here and just use
|
||
the regular node }
|
||
dinode:=def_meta_node(def);
|
||
list.concat(dinode);
|
||
|
||
{ we have to attach the debug info to the definition instruction of the
|
||
proc }
|
||
procdeftai:=nil;
|
||
if in_currentunit then
|
||
begin
|
||
procdeftai:=def.procstarttai;
|
||
if (procdeftai.typ<>ait_llvmdecl) or
|
||
(taillvmdecl(procdeftai).def<>def) then
|
||
internalerror(2022022010);
|
||
taillvmdecl(procdeftai).addinsmetadata(tai_llvmmetadatareferenceoperand.createreferenceto('dbg',dinode));
|
||
end;
|
||
|
||
dinode.addstring('name',symdebugname(def.procsym));
|
||
if assigned(def.struct) and
|
||
not is_objc_class_or_protocol(def.struct) then
|
||
begin
|
||
if is_implicit_pointer_object_type(def.struct) then
|
||
dinode.addmetadatarefto('scope',def_meta_class_struct(tobjectdef(def.struct)))
|
||
else
|
||
dinode.addmetadatarefto('scope',def_meta_node(def.struct));
|
||
try_add_file_metaref(dinode,def.fileinfo,false);
|
||
end
|
||
else
|
||
try_add_file_metaref(dinode,def.fileinfo,true);
|
||
if not(cs_debuginfo in current_settings.moduleswitches) then
|
||
begin
|
||
def.dbg_state:=dbg_state_written;
|
||
exit;
|
||
end;
|
||
|
||
is_virtual:=
|
||
(([po_abstractmethod, po_virtualmethod, po_overridingmethod]*def.procoptions)<>[]) and
|
||
not is_objc_class_or_protocol(def.struct) and
|
||
not is_objectpascal_helper(def.struct);
|
||
adddispflags(dinode,in_currentunit,is_virtual);
|
||
if is_virtual then
|
||
begin
|
||
{ the sizeof(pint) is a bit iffy, since vmtmethodoffset() calculates
|
||
using a combination of voidcodepointer.size, voidpointer.size, and
|
||
sizeof(pint). But that's what the debugger will use }
|
||
dinode.addint64('virtualIndex',tobjectdef(def.owner.defowner).vmtmethodoffset(def.extnumber) div sizeof(pint));
|
||
{$ifdef extdebug}
|
||
if (tobjectdef(def.owner.defowner).vmtmethodoffset(def.extnumber) mod sizeof(pint))<>0 then
|
||
internalerror(2022043001);
|
||
{$endif}
|
||
end;
|
||
adddiflags(dinode,in_currentunit);
|
||
|
||
dinode.addmetadatarefto('unit',fcunode);
|
||
ditypenode:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DISubroutineType);
|
||
ditypenode.addmetadatarefto('types',getabstractprocdeftypes(list,def));
|
||
list.concat(ditypenode);
|
||
dinode.addmetadatarefto('type',ditypenode);
|
||
|
||
(*
|
||
if assigned(def.parast) then
|
||
begin
|
||
{ First insert self, because gdb uses the fact whether or not the
|
||
first parameter of a method is artificial to distinguish static
|
||
from regular methods. }
|
||
|
||
{ fortunately, self is the always the first parameter in the
|
||
paralist, since it has the lowest paranr. Note that this is not
|
||
true for Objective-C, but those methods are detected in
|
||
another way (by reading the ObjC run time information) }
|
||
write_symtable_parasyms(current_asmdata.asmlists[al_dwarf_info],def.paras);
|
||
end;
|
||
{ local type defs and vars should not be written
|
||
inside the main proc }
|
||
if in_currentunit and
|
||
assigned(def.localst) and
|
||
(def.localst.symtabletype=localsymtable) then
|
||
write_symtable_syms(current_asmdata.asmlists[al_dwarf_info],def.localst);
|
||
|
||
{ last write the types from this procdef }
|
||
if assigned(def.parast) then
|
||
write_symtable_defs(current_asmdata.asmlists[al_dwarf_info],def.parast);
|
||
{ only try to write the localst if the routine is implemented here }
|
||
if in_currentunit and
|
||
assigned(def.localst) and
|
||
(def.localst.symtabletype=localsymtable) then
|
||
begin
|
||
write_symtable_defs(current_asmdata.asmlists[al_dwarf_info],def.localst);
|
||
{ Write nested procedures -- disabled, see scope check at the
|
||
beginning; currently, these are still written in the global
|
||
scope. }
|
||
// write_symtable_procdefs(current_asmdata.asmlists[al_dwarf_info],def.localst);
|
||
end;
|
||
|
||
finish_children;
|
||
*)
|
||
def.dbg_state:=dbg_state_written;
|
||
end;
|
||
|
||
procedure TDebugInfoLLVM.adddefinitionlocal(dinode: tai_llvmspecialisedmetadatanode; definition, local, usedispflags: boolean; out dispFlags: tsymstr);
|
||
begin
|
||
dispflags:='';
|
||
if not usedispflags then
|
||
begin
|
||
dinode.addboolean('isDefinition',definition);
|
||
if definition then
|
||
begin
|
||
dinode.addboolean('isLocal',local);
|
||
end;
|
||
exit;
|
||
end;
|
||
|
||
if definition then
|
||
begin
|
||
dispflags:='DISPFlagDefinition';
|
||
if local then
|
||
dispflags:=dispflags+'|DISPFlagLocalToUnit';
|
||
end;
|
||
end;
|
||
|
||
|
||
function TDebugInfoLLVM.get_symlist_sym_offset(symlist: ppropaccesslistitem; out sym: tabstractvarsym; out offset: pint): boolean;
|
||
(*
|
||
var
|
||
elesize : pint;
|
||
currdef : tdef;
|
||
indirection: boolean;
|
||
*)
|
||
begin
|
||
result:=false;
|
||
(*
|
||
if not assigned(symlist) then
|
||
exit;
|
||
sym:=nil;
|
||
offset:=0;
|
||
currdef:=nil;
|
||
indirection:=false;
|
||
repeat
|
||
case symlist^.sltype of
|
||
sl_load:
|
||
begin
|
||
if assigned(sym) then
|
||
internalerror(2009031203);
|
||
if not(symlist^.sym.typ in [paravarsym,localvarsym,staticvarsym,fieldvarsym]) then
|
||
{ can't handle... }
|
||
exit;
|
||
sym:=tabstractvarsym(symlist^.sym);
|
||
currdef:=tabstractvarsym(sym).vardef;
|
||
if ((sym.typ=paravarsym) and
|
||
paramanager.push_addr_param(tparavarsym(sym).varspez,sym.vardef,tprocdef(sym.owner.defowner).proccalloption)) then
|
||
indirection:=true;
|
||
end;
|
||
sl_subscript:
|
||
begin
|
||
if not assigned(currdef) then
|
||
internalerror(2009031301);
|
||
if (symlist^.sym.typ<>fieldvarsym) then
|
||
internalerror(2009031202);
|
||
{ can't handle offsets with indirections yet }
|
||
if indirection then
|
||
exit;
|
||
if is_packed_record_or_object(currdef) then
|
||
begin
|
||
{ can't calculate the address of a non-byte aligned field }
|
||
if (tfieldvarsym(symlist^.sym).fieldoffset mod 8) <> 0 then
|
||
exit;
|
||
inc(offset,tfieldvarsym(symlist^.sym).fieldoffset div 8)
|
||
end
|
||
else
|
||
inc(offset,tfieldvarsym(symlist^.sym).fieldoffset);
|
||
currdef:=tfieldvarsym(symlist^.sym).vardef;
|
||
end;
|
||
sl_absolutetype,
|
||
sl_typeconv:
|
||
begin
|
||
currdef:=symlist^.def;
|
||
{ ignore, these don't change the address }
|
||
end;
|
||
sl_vec:
|
||
begin
|
||
if not assigned(currdef) or
|
||
(currdef.typ<>arraydef) then
|
||
internalerror(2009031201);
|
||
{ can't handle offsets with indirections yet }
|
||
if indirection then
|
||
exit;
|
||
if not is_packed_array(currdef) then
|
||
elesize:=tarraydef(currdef).elesize
|
||
else
|
||
begin
|
||
elesize:=tarraydef(currdef).elepackedbitsize;
|
||
{ can't calculate the address of a non-byte aligned element }
|
||
if (elesize mod 8)<>0 then
|
||
exit;
|
||
elesize:=elesize div 8;
|
||
end;
|
||
inc(offset,(symlist^.value.svalue-tarraydef(currdef).lowrange)*elesize);
|
||
currdef:=tarraydef(currdef).elementdef;
|
||
end;
|
||
else
|
||
internalerror(2009031403);
|
||
end;
|
||
symlist:=symlist^.next;
|
||
until not assigned(symlist);
|
||
if not assigned(sym) then
|
||
internalerror(2009031205);
|
||
result:=true;
|
||
*)
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appendsym_var(list:TAsmList;sym:tabstractnormalvarsym);
|
||
begin
|
||
// appendsym_var_with_name_type_offset(list,sym,symname(sym, false),sym.vardef,0,[]);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appendsym_var_with_name_type_offset(list:TAsmList; sym:tabstractnormalvarsym; const name: TSymStr; def: tdef; offset: pint(*; const flags: tdwarfvarsymflags*));
|
||
(*
|
||
var
|
||
templist : TAsmList;
|
||
blocksize,size_of_int : longint;
|
||
tag : tdwarf_tag;
|
||
has_high_reg : boolean;
|
||
dreg,dreghigh : shortint;
|
||
{$ifdef i8086}
|
||
has_segment_sym_name : boolean=false;
|
||
segment_sym_name : TSymStr='';
|
||
segment_reg: TRegister=NR_NO;
|
||
{$endif i8086}
|
||
*)
|
||
begin
|
||
(*
|
||
if vo_is_external in sym.varoptions then
|
||
exit;
|
||
blocksize:=0;
|
||
dreghigh:=0;
|
||
|
||
{ There is no space allocated for not referenced locals }
|
||
if (sym.owner.symtabletype=localsymtable) and (sym.refs=0) then
|
||
exit;
|
||
|
||
templist:=TAsmList.create;
|
||
|
||
case sym.localloc.loc of
|
||
LOC_REGISTER,
|
||
LOC_CREGISTER,
|
||
LOC_MMREGISTER,
|
||
LOC_CMMREGISTER,
|
||
LOC_FPUREGISTER,
|
||
LOC_CFPUREGISTER :
|
||
begin
|
||
{ dwarf_reg_no_error might return -1
|
||
in case the register variable has been optimized out }
|
||
dreg:=dwarf_reg_no_error(sym.localloc.register);
|
||
has_high_reg:=(sym.localloc.loc in [LOC_REGISTER,LOC_CREGISTER]) and (sym.localloc.registerhi<>NR_NO);
|
||
if has_high_reg then
|
||
dreghigh:=dwarf_reg_no_error(sym.localloc.registerhi);
|
||
if dreghigh=-1 then
|
||
has_high_reg:=false;
|
||
if (sym.localloc.loc in [LOC_REGISTER,LOC_CREGISTER]) and
|
||
(sym.typ=paravarsym) and
|
||
paramanager.push_addr_param(sym.varspez,sym.vardef,tprocdef(sym.owner.defowner).proccalloption) and
|
||
not(vo_has_local_copy in sym.varoptions) and
|
||
not is_open_string(sym.vardef) and (dreg>=0) then
|
||
begin
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_bregx)));
|
||
templist.concat(tai_const.create_uleb128bit(dreg));
|
||
templist.concat(tai_const.create_sleb128bit(0));
|
||
blocksize:=1+Lengthuleb128(dreg)+LengthSleb128(0);
|
||
end
|
||
else
|
||
begin
|
||
if has_high_reg then
|
||
begin
|
||
templist.concat(tai_comment.create(strpnew('high:low reg pair variable')));
|
||
size_of_int:=sizeof(aint);
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_regx)));
|
||
templist.concat(tai_const.create_uleb128bit(dreg));
|
||
blocksize:=1+Lengthuleb128(dreg);
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_piece)));
|
||
templist.concat(tai_const.create_uleb128bit(size_of_int));
|
||
blocksize:=blocksize+1+Lengthuleb128(size_of_int);
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_regx)));
|
||
templist.concat(tai_const.create_uleb128bit(dreghigh));
|
||
blocksize:=blocksize+1+Lengthuleb128(dreghigh);
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_piece)));
|
||
templist.concat(tai_const.create_uleb128bit(size_of_int));
|
||
blocksize:=blocksize+1+Lengthuleb128(size_of_int);
|
||
end
|
||
else if (dreg>=0) then
|
||
begin
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_regx)));
|
||
templist.concat(tai_const.create_uleb128bit(dreg));
|
||
blocksize:=1+Lengthuleb128(dreg);
|
||
end;
|
||
end;
|
||
end;
|
||
else
|
||
begin
|
||
case sym.typ of
|
||
staticvarsym:
|
||
begin
|
||
if vo_is_thread_var in sym.varoptions then
|
||
begin
|
||
if tf_section_threadvars in target_info.flags then
|
||
begin
|
||
case sizeof(puint) of
|
||
2:
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_const2u)));
|
||
4:
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_const4u)));
|
||
8:
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_const8u)));
|
||
else
|
||
Internalerror(2019100501);
|
||
end;
|
||
{$push}
|
||
{$warn 6018 off} { Unreachable code due to compile time evaluation }
|
||
templist.concat(tai_const.Create_type_name(aitconst_dtpoff,sym.mangledname,0));
|
||
{ so far, aitconst_dtpoff is solely 32 bit }
|
||
if (sizeof(puint)=8) and (target_info.endian=endian_little) then
|
||
templist.concat(tai_const.create_32bit(0));
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_GNU_push_tls_address)));
|
||
if (sizeof(puint)=8) and (target_info.endian=endian_big) then
|
||
templist.concat(tai_const.create_32bit(0));
|
||
{$pop}
|
||
|
||
blocksize:=2+sizeof(puint);
|
||
end
|
||
else
|
||
begin
|
||
{ TODO: !!! FIXME: dwarf for thread vars !!!}
|
||
{ This is only a minimal change to at least be able to get a value
|
||
in only one thread is present PM 2014-11-21, like for stabs format }
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_addr)));
|
||
templist.concat(tai_const.Create_type_name(aitconst_ptr_unaligned,sym.mangledname,
|
||
offset+sizeof(pint)));
|
||
blocksize:=1+sizeof(puint);
|
||
end;
|
||
end
|
||
else
|
||
begin
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_addr)));
|
||
templist.concat(tai_const.Create_type_name(aitconst_ptr_unaligned,sym.mangledname,offset));
|
||
blocksize:=1+sizeof(puint);
|
||
{$ifdef i8086}
|
||
segment_sym_name:=sym.mangledname;
|
||
has_segment_sym_name:=true;
|
||
{$endif i8086}
|
||
end;
|
||
end;
|
||
paravarsym,
|
||
localvarsym:
|
||
begin
|
||
{ Happens when writing debug info for paras of procdefs not
|
||
implemented in the current module. Can't add a general check
|
||
for LOC_INVALID above, because staticvarsyms may also have it.
|
||
}
|
||
if sym.localloc.loc<> LOC_INVALID then
|
||
begin
|
||
if is_fbreg(sym.localloc.reference.base) then
|
||
begin
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_fbreg)));
|
||
templist.concat(tai_const.create_sleb128bit(sym.localloc.reference.offset+offset));
|
||
blocksize:=1+Lengthsleb128(sym.localloc.reference.offset+offset);
|
||
end
|
||
else
|
||
begin
|
||
dreg:=dwarf_reg(sym.localloc.reference.base);
|
||
if dreg<=31 then
|
||
begin
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_breg0)+dreg));
|
||
templist.concat(tai_const.create_sleb128bit(sym.localloc.reference.offset+offset));
|
||
blocksize:=1+Lengthsleb128(sym.localloc.reference.offset+offset);
|
||
end
|
||
else
|
||
begin
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_bregx)));
|
||
templist.concat(tai_const.create_uleb128bit(dreg));
|
||
templist.concat(tai_const.create_sleb128bit(sym.localloc.reference.offset+offset));
|
||
blocksize:=1+Lengthuleb128(dreg)+LengthSleb128(sym.localloc.reference.offset+offset);
|
||
end;
|
||
end;
|
||
{$ifdef i8086}
|
||
segment_reg:=sym.localloc.reference.segment;
|
||
{$endif i8086}
|
||
{$ifndef gdb_supports_DW_AT_variable_parameter}
|
||
{ Parameters which are passed by reference. (var and the like)
|
||
Hide the reference-pointer and dereference the pointer
|
||
in the DW_AT_location block.
|
||
}
|
||
if (sym.typ=paravarsym) and
|
||
paramanager.push_addr_param(sym.varspez,sym.vardef,tprocdef(sym.owner.defowner).proccalloption) and
|
||
not(vo_has_local_copy in sym.varoptions) and
|
||
not is_open_string(sym.vardef) then
|
||
begin
|
||
templist.concat(tai_const.create_8bit(ord(DW_OP_deref)));
|
||
inc(blocksize);
|
||
end
|
||
{$endif not gdb_supports_DW_AT_variable_parameter}
|
||
end;
|
||
end
|
||
else
|
||
internalerror(200601288);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
{ function results must not be added to the parameter list,
|
||
as they are not part of the signature of the function
|
||
(gdb automatically adds them according to the ABI specifications
|
||
when calling the function)
|
||
}
|
||
if (sym.typ=paravarsym) and
|
||
not(dvf_force_local_var in flags) and
|
||
not(vo_is_funcret in sym.varoptions) then
|
||
tag:=DW_TAG_formal_parameter
|
||
else
|
||
tag:=DW_TAG_variable;
|
||
|
||
{ must be parasym of externally implemented procdef, but
|
||
the parasymtable can con also contain e.g. absolutevarsyms
|
||
-> check symtabletype}
|
||
if (sym.owner.symtabletype=parasymtable) and
|
||
(sym.localloc.loc=LOC_INVALID) then
|
||
begin
|
||
if (sym.owner.symtabletype<>parasymtable) then
|
||
internalerror(2009101001);
|
||
append_entry(tag,false,[
|
||
DW_AT_name,DW_FORM_string,name+#0
|
||
{
|
||
DW_AT_decl_file,DW_FORM_data1,0,
|
||
DW_AT_decl_line,DW_FORM_data1,
|
||
}
|
||
])
|
||
end
|
||
else if not(sym.localloc.loc in [LOC_REGISTER,LOC_CREGISTER,LOC_MMREGISTER,
|
||
LOC_CMMREGISTER,LOC_FPUREGISTER,LOC_CFPUREGISTER]) and
|
||
((sym.owner.symtabletype = globalsymtable) or
|
||
(sp_static in sym.symoptions) or
|
||
(vo_is_public in sym.varoptions)) then
|
||
append_entry(tag,false,[
|
||
DW_AT_name,DW_FORM_string,name+#0,
|
||
{
|
||
DW_AT_decl_file,DW_FORM_data1,0,
|
||
DW_AT_decl_line,DW_FORM_data1,
|
||
}
|
||
DW_AT_external,DW_FORM_flag,true,
|
||
{ data continues below }
|
||
DW_AT_location,DW_FORM_block1,blocksize
|
||
])
|
||
{$ifdef gdb_supports_DW_AT_variable_parameter}
|
||
else if (sym.typ=paravarsym) and
|
||
paramanager.push_addr_param(sym.varspez,sym.vardef,tprocdef(sym.owner.defowner).proccalloption) and
|
||
not(vo_has_local_copy in sym.varoptions) and
|
||
not is_open_string(sym.vardef) then
|
||
append_entry(tag,false,[
|
||
DW_AT_name,DW_FORM_string,name+#0,
|
||
DW_AT_variable_parameter,DW_FORM_flag,true,
|
||
{
|
||
DW_AT_decl_file,DW_FORM_data1,0,
|
||
DW_AT_decl_line,DW_FORM_data1,
|
||
}
|
||
{ data continues below }
|
||
DW_AT_location,DW_FORM_block1,blocksize
|
||
])
|
||
{$endif gdb_supports_DW_AT_variable_parameter}
|
||
else
|
||
append_entry(tag,false,[
|
||
DW_AT_name,DW_FORM_string,name+#0,
|
||
{
|
||
DW_AT_decl_file,DW_FORM_data1,0,
|
||
DW_AT_decl_line,DW_FORM_data1,
|
||
}
|
||
{ data continues below }
|
||
DW_AT_location,DW_FORM_block1,blocksize
|
||
]);
|
||
{ append block data }
|
||
current_asmdata.asmlists[al_dwarf_info].concatlist(templist);
|
||
{ Mark self as artificial for methods, because gdb uses the fact
|
||
whether or not the first parameter of a method is artificial to
|
||
distinguish regular from static methods (since there are no
|
||
no vo_is_self parameters for static methods, we don't have to check
|
||
that). }
|
||
if (vo_is_self in sym.varoptions) then
|
||
append_attribute(DW_AT_artificial,DW_FORM_flag,[true]);
|
||
append_labelentry_ref(DW_AT_type,def_dwarf_lab(def));
|
||
{$ifdef i8086}
|
||
if has_segment_sym_name then
|
||
append_seg_name(segment_sym_name)
|
||
else if segment_reg<>NR_NO then
|
||
append_seg_reg(segment_reg);
|
||
{$endif i8086}
|
||
|
||
templist.free;
|
||
|
||
finish_entry;
|
||
*)
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appendsym_staticvar(list:TAsmList;sym:tstaticvarsym);
|
||
var
|
||
decl: taillvmdecl;
|
||
globalvarexpression, globalvar: tai_llvmspecialisedmetadatanode;
|
||
dispflags: tsymstr;
|
||
islocal: boolean;
|
||
begin
|
||
decl:=staticvarsym_get_decl(sym);
|
||
if not assigned(decl) then
|
||
begin
|
||
list.concat(tai_comment.create(strpnew('no declaration found for '+sym.mangledname)));
|
||
exit;
|
||
end;
|
||
globalvar:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DIGlobalVariable);
|
||
list.concat(globalvar);
|
||
|
||
globalvarexpression:=tai_llvmspecialisedmetadatanode.create(tspecialisedmetadatanodekind.DIGlobalVariableExpression);
|
||
globalvarexpression.addmetadatarefto('var',globalvar);
|
||
globalvarexpression.addmetadatarefto('expr',femptyexpression);
|
||
list.concat(globalvarexpression);
|
||
fglobals.addvalue(llvm_getmetadatareftypedconst(globalvarexpression));
|
||
|
||
decl.addinsmetadata(tai_llvmmetadatareferenceoperand.createreferenceto('dbg',globalvarexpression));
|
||
|
||
globalvar.addstring('name',symname(sym,false));
|
||
if not assigned(sym.owner.defowner) then
|
||
globalvar.addmetadatarefto('scope',fcunode)
|
||
else
|
||
globalvar.addmetadatarefto('scope',def_meta_node(tdef(sym.owner.defowner)));
|
||
try_add_file_metaref(globalvar,sym.fileinfo,false);
|
||
globalvar.addmetadatarefto('type',def_meta_node(sym.vardef));
|
||
|
||
islocal:=not(
|
||
((sym.owner.symtabletype = globalsymtable) or
|
||
(sp_static in sym.symoptions) or
|
||
(vo_is_public in sym.varoptions))
|
||
);
|
||
|
||
adddefinitionlocal(globalvar,not(vo_is_external in sym.varoptions),islocal,false,dispflags);
|
||
if dispflags<>'' then
|
||
globalvar.addenum('spFlags',dispflags);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appendsym_localvar(list:TAsmList;sym:tlocalvarsym);
|
||
begin
|
||
// appendsym_var(list,sym);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appendsym_paravar(list:TAsmList;sym:tparavarsym);
|
||
begin
|
||
// appendsym_var(list,sym);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appendsym_fieldvar(list:TAsmList;sym: tfieldvarsym);
|
||
begin
|
||
appendsym_fieldvar_with_name_offset(list,sym,symname(sym, false),sym.vardef,0);
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appendsym_fieldvar_with_name_offset(list:TAsmList;sym: tfieldvarsym;const name: string; def: tdef; offset: pint);
|
||
var
|
||
bitoffset,
|
||
fieldoffset,
|
||
fieldnatsize: asizeint;
|
||
begin
|
||
(*
|
||
if (sp_static in sym.symoptions) or
|
||
(sym.visibility=vis_hidden) then
|
||
exit;
|
||
|
||
if (tabstractrecordsymtable(sym.owner).usefieldalignment<>bit_alignment) or
|
||
{ only ordinals are bitpacked }
|
||
not is_ordinal(sym.vardef) then
|
||
begin
|
||
{ other kinds of fields can however also appear in a bitpacked }
|
||
{ record, and then their offset is also specified in bits rather }
|
||
{ than in bytes }
|
||
if (tabstractrecordsymtable(sym.owner).usefieldalignment<>bit_alignment) then
|
||
fieldoffset:=sym.fieldoffset
|
||
else
|
||
fieldoffset:=sym.fieldoffset div 8;
|
||
inc(fieldoffset,offset);
|
||
append_entry(DW_TAG_member,false,[
|
||
DW_AT_name,DW_FORM_string,name+#0,
|
||
DW_AT_data_member_location,DW_FORM_block1,1+lengthuleb128(fieldoffset)
|
||
]);
|
||
end
|
||
else
|
||
begin
|
||
if (sym.vardef.packedbitsize > 255) then
|
||
internalerror(2007061201);
|
||
|
||
{ we don't bitpack according to the ABI, but as close as }
|
||
{ possible, i.e., equivalent to gcc's }
|
||
{ __attribute__((__packed__)), which is also what gpc }
|
||
{ does. }
|
||
fieldnatsize:=max(sizeof(pint),sym.vardef.size);
|
||
fieldoffset:=(sym.fieldoffset div (fieldnatsize*8)) * fieldnatsize;
|
||
inc(fieldoffset,offset);
|
||
bitoffset:=sym.fieldoffset mod (fieldnatsize*8);
|
||
if (target_info.endian=endian_little) then
|
||
bitoffset:=(fieldnatsize*8)-bitoffset-sym.vardef.packedbitsize;
|
||
append_entry(DW_TAG_member,false,[
|
||
DW_AT_name,DW_FORM_string,symname(sym, false)+#0,
|
||
{ gcc also generates both a bit and byte size attribute }
|
||
{ we don't support ordinals >= 256 bits }
|
||
DW_AT_byte_size,DW_FORM_data1,fieldnatsize,
|
||
{ nor >= 256 bits (not yet, anyway, see IE above) }
|
||
DW_AT_bit_size,DW_FORM_data1,sym.vardef.packedbitsize,
|
||
{ data1 and data2 are unsigned, bitoffset can also be negative }
|
||
DW_AT_bit_offset,DW_FORM_data4,bitoffset,
|
||
DW_AT_data_member_location,DW_FORM_block1,1+lengthuleb128(fieldoffset)
|
||
]);
|
||
end;
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_8bit(ord(DW_OP_plus_uconst)));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_uleb128bit(fieldoffset));
|
||
if (sym.owner.symtabletype in [objectsymtable,recordsymtable]) then
|
||
append_visibility(sym.visibility);
|
||
|
||
append_labelentry_ref(DW_AT_type,def_dwarf_lab(def));
|
||
finish_entry;
|
||
*)
|
||
end;
|
||
|
||
procedure TDebugInfoLLVM.appendsym_const(list:TAsmList;sym:tconstsym);
|
||
begin
|
||
appendsym_const_member(list,sym,false);
|
||
end;
|
||
|
||
procedure TDebugInfoLLVM.appendsym_const_member(list:TAsmList;sym:tconstsym;ismember:boolean);
|
||
var
|
||
i,
|
||
size: aint;
|
||
usedef: tdef;
|
||
begin
|
||
(*
|
||
{ These are default values of parameters. These should be encoded
|
||
via DW_AT_default_value, not as a separate sym. Moreover, their
|
||
type is not available when writing the debug info for external
|
||
procedures.
|
||
}
|
||
if (sym.owner.symtabletype=parasymtable) then
|
||
exit;
|
||
|
||
if ismember then
|
||
append_entry(DW_TAG_member,false,[
|
||
DW_AT_name,DW_FORM_string,symname(sym, false)+#0,
|
||
{ The DW_AT_declaration tag is invalid according to the DWARF specifications.
|
||
But gcc adds this to static const members and gdb checks
|
||
for this flag. So we have to set it also.
|
||
}
|
||
DW_AT_declaration,DW_FORM_flag,true,
|
||
DW_AT_external,DW_FORM_flag,true
|
||
])
|
||
else
|
||
append_entry(DW_TAG_variable,false,[
|
||
DW_AT_name,DW_FORM_string,symname(sym, false)+#0
|
||
]);
|
||
{ for string constants, constdef isn't set because they have no real type }
|
||
case sym.consttyp of
|
||
conststring:
|
||
begin
|
||
{ if DW_FORM_string is used below one day, this usedef should
|
||
probably become nil }
|
||
{ note: < 255 instead of <= 255 because we have to store the
|
||
entire length of the string as well, and 256 does not fit in
|
||
a byte }
|
||
if (sym.value.len<255) then
|
||
usedef:=cshortstringtype
|
||
else
|
||
usedef:=clongstringtype;
|
||
end;
|
||
constresourcestring,
|
||
constwstring:
|
||
usedef:=nil;
|
||
else
|
||
usedef:=sym.constdef;
|
||
end;
|
||
if assigned(usedef) then
|
||
append_labelentry_ref(DW_AT_type,def_dwarf_lab(usedef));
|
||
AddConstToAbbrev(ord(DW_AT_const_value));
|
||
case sym.consttyp of
|
||
conststring:
|
||
begin
|
||
{ DW_FORM_string isn't supported yet by the Pascal value printer
|
||
-> create a string using raw bytes }
|
||
if (sym.value.len<255) then
|
||
begin
|
||
AddConstToAbbrev(ord(DW_FORM_block1));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_8bit(sym.value.len+1));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_8bit(sym.value.len));
|
||
end
|
||
else
|
||
begin
|
||
AddConstToAbbrev(ord(DW_FORM_block));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_uleb128bit(sym.value.len+sizesinttype.size));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.Create_sizeint_unaligned(sym.value.len));
|
||
end;
|
||
i:=0;
|
||
size:=sym.value.len;
|
||
while(i<size) do
|
||
begin
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_8bit((pbyte(sym.value.valueptr+i)^)));
|
||
inc(i);
|
||
end;
|
||
end;
|
||
constguid,
|
||
constset:
|
||
begin
|
||
AddConstToAbbrev(ord(DW_FORM_block1));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_8bit(usedef.size));
|
||
i:=0;
|
||
size:=sym.constdef.size;
|
||
while (i<size) do
|
||
begin
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_8bit((pbyte(sym.value.valueptr+i)^)));
|
||
inc(i);
|
||
end;
|
||
end;
|
||
constwstring,
|
||
constresourcestring:
|
||
begin
|
||
{ write dummy for now }
|
||
AddConstToAbbrev(ord(DW_FORM_string));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_string.create(''));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_8bit(0));
|
||
end;
|
||
constord:
|
||
begin
|
||
if (sym.value.valueord<0) then
|
||
begin
|
||
AddConstToAbbrev(ord(DW_FORM_sdata));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_sleb128bit(sym.value.valueord.svalue));
|
||
end
|
||
else
|
||
begin
|
||
AddConstToAbbrev(ord(DW_FORM_udata));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_uleb128bit(sym.value.valueord.uvalue));
|
||
end;
|
||
end;
|
||
constnil:
|
||
begin
|
||
{$ifdef cpu64bitaddr}
|
||
AddConstToAbbrev(ord(DW_FORM_data8));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_64bit_unaligned(0));
|
||
{$else cpu64bitaddr}
|
||
AddConstToAbbrev(ord(DW_FORM_data4));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_32bit_unaligned(0));
|
||
{$endif cpu64bitaddr}
|
||
end;
|
||
constpointer:
|
||
begin
|
||
{$ifdef cpu64bitaddr}
|
||
AddConstToAbbrev(ord(DW_FORM_data8));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_64bit_unaligned(int64(sym.value.valueordptr)));
|
||
{$else cpu64bitaddr}
|
||
AddConstToAbbrev(ord(DW_FORM_data4));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_32bit_unaligned(longint(sym.value.valueordptr)));
|
||
{$endif cpu64bitaddr}
|
||
end;
|
||
constreal:
|
||
begin
|
||
AddConstToAbbrev(ord(DW_FORM_block1));
|
||
case tfloatdef(sym.constdef).floattype of
|
||
s32real:
|
||
begin
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_8bit(4));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_realconst.create_s32real(pbestreal(sym.value.valueptr)^));
|
||
end;
|
||
s64real:
|
||
begin
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_8bit(8));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_realconst.create_s64real(pbestreal(sym.value.valueptr)^));
|
||
end;
|
||
s64comp,
|
||
s64currency:
|
||
begin
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_8bit(8));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_64bit_unaligned(trunc(pbestreal(sym.value.valueptr)^)));
|
||
end;
|
||
s80real,
|
||
sc80real:
|
||
begin
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_const.create_8bit(sym.constdef.size));
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_realconst.create_s80real(pextended(sym.value.valueptr)^,sym.constdef.size));
|
||
end;
|
||
else
|
||
internalerror(200601291);
|
||
end;
|
||
end;
|
||
else
|
||
internalerror(200601292);
|
||
end;
|
||
finish_entry;
|
||
*)
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appendsym_label(list:TAsmList;sym: tlabelsym);
|
||
begin
|
||
{ ignore label syms for now, the problem is that a label sym
|
||
can have more than one label associated e.g. in case of
|
||
an inline procedure expansion }
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appendsym_property(list:TAsmList;sym: tpropertysym);
|
||
var
|
||
symlist: ppropaccesslistitem;
|
||
tosym: tabstractvarsym;
|
||
offset: pint;
|
||
begin
|
||
(*
|
||
if assigned(sym.propaccesslist[palt_read]) and
|
||
not assigned(sym.propaccesslist[palt_read].procdef) then
|
||
symlist:=sym.propaccesslist[palt_read].firstsym
|
||
else
|
||
{ can't handle }
|
||
exit;
|
||
|
||
if not get_symlist_sym_offset(symlist,tosym,offset) then
|
||
exit;
|
||
|
||
if not (tosym.owner.symtabletype in [objectsymtable,recordsymtable]) then
|
||
begin
|
||
if (tosym.typ=fieldvarsym) then
|
||
internalerror(2009031404);
|
||
appendsym_var_with_name_type_offset(list,tabstractnormalvarsym(tosym),symname(sym, false),sym.propdef,offset,[])
|
||
end
|
||
else
|
||
appendsym_fieldvar_with_name_offset(list,tfieldvarsym(tosym),symname(sym, false),sym.propdef,offset)
|
||
*)
|
||
end;
|
||
|
||
|
||
function TDebugInfoLLVM.symdebugname(sym: tsym): TSymStr;
|
||
begin
|
||
if ds_dwarf_cpp in current_settings.debugswitches then
|
||
begin
|
||
if sym.visibility=vis_hidden then
|
||
result:=copy(sym.RealName,length('$hidden')+1,length(sym.RealName))
|
||
else
|
||
begin
|
||
result:=sym.RealName;
|
||
if (result<>'') and
|
||
(result[1]='$') then
|
||
delete(result,1,1);
|
||
end
|
||
end
|
||
else if sym.visibility=vis_hidden then
|
||
result:=copy(sym.name,length('hidden')+1,length(sym.name))
|
||
else
|
||
result:=sym.name
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appendsym_type(list:TAsmList;sym: ttypesym);
|
||
begin
|
||
{ just queue the def if needed, beforeappenddef will
|
||
emit the typedef if necessary }
|
||
get_def_metatai(sym.typedef);
|
||
{
|
||
if FindUnitSymtable(sym.Owner).iscurrentunit then
|
||
fretainedtypes.addvalue(def_meta_ref(sym.typedef));
|
||
}
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.appendsym_absolute(list:TAsmList;sym:tabsolutevarsym);
|
||
(*
|
||
var
|
||
templist : TAsmList;
|
||
blocksize : longint;
|
||
symlist : ppropaccesslistitem;
|
||
tosym: tabstractvarsym;
|
||
offset: pint;
|
||
flags: tdwarfvarsymflags;
|
||
*)
|
||
begin
|
||
(*
|
||
templist:=TAsmList.create;
|
||
case tabsolutevarsym(sym).abstyp of
|
||
toaddr :
|
||
begin
|
||
{ MWE: replaced ifdef i368 }
|
||
{
|
||
if target_cpu = cpu_i386 then
|
||
begin
|
||
{ in theory, we could write a DW_AT_segment entry here for sym.absseg,
|
||
however I doubt that gdb supports this (FK) }
|
||
end;
|
||
}
|
||
templist.concat(tai_const.create_8bit(3));
|
||
{$ifdef avr}
|
||
// Add $800000 to indicate that the address is in memory space
|
||
templist.concat(tai_const.create_int_dataptr_unaligned(sym.addroffset + $800000, aitconst_ptr_unaligned));
|
||
{$else}
|
||
templist.concat(tai_const.create_int_dataptr_unaligned(sym.addroffset));
|
||
{$endif}
|
||
blocksize:=1+sizeof(puint);
|
||
end;
|
||
toasm :
|
||
begin
|
||
templist.concat(tai_const.create_8bit(3));
|
||
templist.concat(tai_const.create_type_name(aitconst_ptr_unaligned,sym.mangledname,0));
|
||
blocksize:=1+sizeof(puint);
|
||
end;
|
||
tovar:
|
||
begin
|
||
symlist:=tabsolutevarsym(sym).ref.firstsym;
|
||
if get_symlist_sym_offset(symlist,tosym,offset) then
|
||
begin
|
||
if (tosym.typ=fieldvarsym) then
|
||
internalerror(2009031402);
|
||
flags:=[];
|
||
if (sym.owner.symtabletype=localsymtable) then
|
||
include(flags,dvf_force_local_var);
|
||
appendsym_var_with_name_type_offset(list,tabstractnormalvarsym(tosym),symname(sym, false),tabstractvarsym(sym).vardef,offset,flags);
|
||
end;
|
||
templist.free;
|
||
exit;
|
||
end;
|
||
end;
|
||
|
||
append_entry(DW_TAG_variable,false,[
|
||
DW_AT_name,DW_FORM_string,symname(sym, false)+#0,
|
||
{
|
||
DW_AT_decl_file,DW_FORM_data1,0,
|
||
DW_AT_decl_line,DW_FORM_data1,
|
||
}
|
||
DW_AT_external,DW_FORM_flag,true,
|
||
{ data continues below }
|
||
DW_AT_location,DW_FORM_block1,blocksize
|
||
]);
|
||
{ append block data }
|
||
current_asmdata.asmlists[al_dwarf_info].concatlist(templist);
|
||
append_labelentry_ref(DW_AT_type,def_dwarf_lab(sym.vardef));
|
||
|
||
templist.free;
|
||
|
||
finish_entry;
|
||
*)
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.beforeappendsym(list:TAsmList;sym:tsym);
|
||
begin
|
||
current_asmdata.asmlists[al_dwarf_info].concat(tai_comment.Create(strpnew('Symbol '+symname(sym, true))));
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.insertmoduleinfo;
|
||
var
|
||
culist: tai_llvmnamedmetadatanode;
|
||
dwarfversionflag: tai_llvmbasemetadatanode;
|
||
objcruntimeversion: longint;
|
||
begin
|
||
ensuremetainit;
|
||
|
||
{ debug info header }
|
||
if ds_dwarf_cpp in current_settings.debugswitches then
|
||
fcunode.addenum('language','DW_LANG_C_plus_plus')
|
||
else
|
||
fcunode.addenum('language','DW_LANG_Pascal83');
|
||
fcunode.addmetadatarefto('file',file_getmetanode(current_filepos.moduleindex,current_filepos.fileindex));
|
||
fcunode.addstring('producer','Free Pascal Compiler '+full_version_string);
|
||
fcunode.addboolean('isOptimized',cs_opt_level2 in current_settings.optimizerswitches);
|
||
if target_info.system in systems_objc_supported then
|
||
begin
|
||
if ([m_objectivec1,m_objectivec2]*current_settings.modeswitches)<>[] then
|
||
if target_info.system in systems_objc_nfabi then
|
||
objcruntimeversion:=2
|
||
else
|
||
objcruntimeversion:=1
|
||
else
|
||
objcruntimeversion:=0;
|
||
fcunode.addint64('runtimeVersion',objcruntimeversion);
|
||
end;
|
||
if cs_debuginfo in current_settings.moduleswitches then
|
||
fcunode.addenum('emissionKind','FullDebug')
|
||
else
|
||
fcunode.addenum('emissionKind','LineTablesOnly');
|
||
if fenums.valuecount<>0 then
|
||
begin
|
||
fcunode.addmetadatarefto('enums',fenums);
|
||
current_asmdata.AsmLists[al_dwarf_info].Concat(fenums);
|
||
end
|
||
else
|
||
begin
|
||
fcunode.addmetadatarefto('enums',nil);
|
||
fenums.free;
|
||
end;
|
||
fenums:=nil;
|
||
if fretainedtypes.valuecount<>0 then
|
||
begin
|
||
fcunode.addmetadatarefto('retainedTypes',fretainedtypes);
|
||
current_asmdata.AsmLists[al_dwarf_info].Concat(fretainedtypes);
|
||
end
|
||
else
|
||
begin
|
||
fcunode.addmetadatarefto('retainedTypes',nil);
|
||
fretainedtypes.free;
|
||
end;
|
||
fretainedtypes:=nil;
|
||
if fglobals.valuecount<>0 then
|
||
begin
|
||
fcunode.addmetadatarefto('globals',fglobals);
|
||
current_asmdata.AsmLists[al_dwarf_info].Concat(fglobals);
|
||
end
|
||
else
|
||
begin
|
||
fcunode.addmetadatarefto('globals',nil);
|
||
fglobals.free;
|
||
end;
|
||
fglobals:=nil;
|
||
current_asmdata.AsmLists[al_dwarf_info].Concat(femptyexpression);
|
||
femptyexpression:=nil;
|
||
current_asmdata.AsmLists[al_dwarf_info].Concat(fderefexpression);
|
||
fderefexpression:=nil;
|
||
|
||
if target_info.system in systems_darwin then
|
||
fcunode.addenum('nameTableKind','GNU');
|
||
current_asmdata.AsmLists[al_dwarf_info].Concat(fcunode);
|
||
culist:=tai_llvmnamedmetadatanode.create('llvm.dbg.cu');
|
||
current_asmdata.AsmLists[al_dwarf_info].Concat(culist);
|
||
culist.addvalue(llvm_getmetadatareftypedconst(fcunode));
|
||
|
||
resetfornewmodule;
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.inserttypeinfo;
|
||
var
|
||
storefilepos : tfileposinfo;
|
||
i : longint;
|
||
def: tdef;
|
||
vardatatype: ttypesym;
|
||
begin
|
||
ensuremetainit;
|
||
storefilepos:=current_filepos;
|
||
current_filepos:=current_module.mainfilepos;
|
||
|
||
vardatatype:=try_search_system_type('TVARDATA');
|
||
if assigned(vardatatype) then
|
||
vardatadef:=trecorddef(vardatatype.typedef);
|
||
|
||
collectglobalsyms;
|
||
|
||
{ write all global/local variables. This will flag all required tdefs }
|
||
if assigned(current_module.globalsymtable) then
|
||
write_symtable_syms(current_asmdata.asmlists[al_dwarf_info],current_module.globalsymtable);
|
||
if assigned(current_module.localsymtable) then
|
||
write_symtable_syms(current_asmdata.asmlists[al_dwarf_info],current_module.localsymtable);
|
||
|
||
{ write all procedures and methods. This will flag all required tdefs }
|
||
if assigned(current_module.globalsymtable) then
|
||
write_symtable_procdefs(current_asmdata.asmlists[al_dwarf_info],current_module.globalsymtable);
|
||
if assigned(current_module.localsymtable) then
|
||
write_symtable_procdefs(current_asmdata.asmlists[al_dwarf_info],current_module.localsymtable);
|
||
|
||
{ reset unit type info flag }
|
||
reset_unit_type_info;
|
||
|
||
{ write used types from the used units }
|
||
write_used_unit_type_info(current_asmdata.asmlists[al_dwarf_info],current_module);
|
||
|
||
{ last write the types from this unit }
|
||
if assigned(current_module.globalsymtable) then
|
||
write_symtable_defs(current_asmdata.asmlists[al_dwarf_info],current_module.globalsymtable);
|
||
if assigned(current_module.localsymtable) then
|
||
write_symtable_defs(current_asmdata.asmlists[al_dwarf_info],current_module.localsymtable);
|
||
|
||
{ write defs not written yet }
|
||
write_remaining_defs_to_write(current_asmdata.asmlists[al_dwarf_info]);
|
||
|
||
{ reset all def debug states for LLVMTypeInfo (which also uses this
|
||
field, to track for which types type info has been inserted already }
|
||
for i:=0 to defnumberlist.count-1 do
|
||
begin
|
||
def := tdef(defnumberlist[i]);
|
||
if assigned(def) then
|
||
def.dbg_state:=dbg_state_unused;
|
||
end;
|
||
|
||
current_filepos:=storefilepos;
|
||
end;
|
||
|
||
|
||
function TDebugInfoLLVM.symname(sym: tsym; manglename: boolean): TSymStr;
|
||
begin
|
||
if (sym.typ=paravarsym) and
|
||
(vo_is_self in tparavarsym(sym).varoptions) then
|
||
{ We use 'this' for regular methods because that's what gdb triggers
|
||
on to automatically search fields. Don't do this for class methods,
|
||
because search class fields is not supported, and gdb 7.0+ fails
|
||
in this case because "this" is not a record in that case (it's a
|
||
pointer to a vmt) }
|
||
if not is_objc_class_or_protocol(tdef(sym.owner.defowner.owner.defowner)) and
|
||
not(po_classmethod in tabstractprocdef(sym.owner.defowner).procoptions) then
|
||
result:='this'
|
||
else
|
||
result:='self'
|
||
else if (sym.typ=typesym) and
|
||
is_objc_class_or_protocol(ttypesym(sym).typedef) then
|
||
result:=tobjectdef(ttypesym(sym).typedef).objextname^
|
||
else if (ds_dwarf_method_class_prefix in current_settings.debugswitches) and
|
||
(sym.typ=procsym) and
|
||
(tprocsym(sym).owner.symtabletype in [objectsymtable,recordsymtable]) then
|
||
begin
|
||
result:=tprocsym(sym).owner.name^+'__';
|
||
if manglename then
|
||
result := result + sym.name
|
||
else
|
||
result := result + symdebugname(sym);
|
||
end
|
||
else
|
||
begin
|
||
if manglename then
|
||
result := sym.name
|
||
else
|
||
result := symdebugname(sym);
|
||
end;
|
||
end;
|
||
|
||
|
||
function TDebugInfoLLVM.visibilitydiflag(vis: tvisibility): TSymStr;
|
||
begin
|
||
case vis of
|
||
vis_hidden,
|
||
vis_private,
|
||
vis_strictprivate:
|
||
result:='DIFlagPrivate';
|
||
vis_protected,
|
||
vis_strictprotected:
|
||
result:='DIFlagProtected';
|
||
vis_published,
|
||
vis_public:
|
||
result:='DIFlagPublic';
|
||
vis_none:
|
||
internalerror(2022050101);
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TDebugInfoLLVM.insertlineinfo(list:TAsmList);
|
||
var
|
||
hp: tai;
|
||
functionscope,
|
||
positionmeta: tai_llvmspecialisedmetadatanode;
|
||
pd: tprocdef;
|
||
procdeffileinfo: tfileposinfo;
|
||
nolineinfolevel : longint;
|
||
firstline: boolean;
|
||
begin
|
||
ensuremetainit;
|
||
hp:=tai(list.first);
|
||
while assigned(hp) and
|
||
((hp.typ<>ait_llvmdecl) or
|
||
(taillvmdecl(hp).def.typ<>procdef)) do
|
||
begin
|
||
hp:=tai(hp.next);
|
||
end;
|
||
if not assigned(hp) then
|
||
exit;
|
||
pd:=tprocdef(taillvmdecl(hp).def);
|
||
procdeffileinfo:=pd.fileinfo;
|
||
{ might trigger for certain kinds of internally generated code }
|
||
if procdeffileinfo.fileindex=0 then
|
||
exit;
|
||
|
||
flocalvarsymmeta.free;
|
||
flocalvarsymmeta:=THashSet.Create((pd.localst.SymList.count+pd.parast.SymList.count)*4+1,true,false);
|
||
|
||
functionscope:=def_meta_node(pd);
|
||
|
||
nolineinfolevel:=0;
|
||
hp:=tai(hp.next);
|
||
firstline:=true;
|
||
while assigned(hp) do
|
||
begin
|
||
case hp.typ of
|
||
ait_marker:
|
||
begin
|
||
case tai_marker(hp).kind of
|
||
mark_NoLineInfoStart:
|
||
inc(nolineinfolevel);
|
||
mark_NoLineInfoEnd:
|
||
dec(nolineinfolevel);
|
||
else
|
||
;
|
||
end;
|
||
end;
|
||
else
|
||
;
|
||
end;
|
||
|
||
if (hp.typ=ait_llvmins) and
|
||
((nolineinfolevel=0) or
|
||
(taillvm(hp).llvmopcode=la_call)) then
|
||
begin
|
||
positionmeta:=nil;
|
||
{ valid file -> add info }
|
||
if (tailineinfo(hp).fileinfo.fileindex<>0) then
|
||
begin
|
||
if firstline and
|
||
(nolineinfolevel=0) then
|
||
begin
|
||
functionscope.addint64('scopeLine',tailineinfo(hp).fileinfo.line);
|
||
firstline:=false;
|
||
end;
|
||
positionmeta:=filepos_getmetanode(tailineinfo(hp).fileinfo,procdeffileinfo,functionscope,nolineinfolevel<>0);
|
||
end
|
||
{ LLVM requires line info for call instructions that may
|
||
potentially be inlined }
|
||
else if taillvm(hp).llvmopcode=la_call then
|
||
begin
|
||
positionmeta:=filepos_getmetanode(tailineinfo(hp).fileinfo,procdeffileinfo,functionscope,true);
|
||
end;
|
||
if assigned(positionmeta) then
|
||
taillvm(hp).addinsmetadata(tai_llvmmetadatareferenceoperand.createreferenceto('dbg',positionmeta));
|
||
if (cs_debuginfo in current_settings.moduleswitches) and
|
||
(taillvm(hp).llvmopcode=la_call) then
|
||
updatelocalvardbginfo(taillvm(hp),pd,functionscope);
|
||
end;
|
||
hp:=tai(hp.next);
|
||
end;
|
||
end;
|
||
|
||
|
||
{****************************************************************************
|
||
****************************************************************************}
|
||
const
|
||
dbg_llvm_info : tdbginfo =
|
||
(
|
||
id : dbg_llvm;
|
||
idtxt : 'LLVM';
|
||
);
|
||
|
||
|
||
initialization
|
||
RegisterDebugInfo(dbg_llvm_info,TDebugInfoLLVM);
|
||
|
||
end.
|