Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add debug info for function parameter types #4804

Merged
merged 2 commits into from
Jan 16, 2025

Conversation

rainers
Copy link
Contributor

@rainers rainers commented Dec 16, 2024

A couple of days ago I added support to mago to evaluate the __debugOverview, __debugExpanded and __debugStringview hooks as free functions instead of struct members (see https://rainers.github.io/visuald/visuald/Debugging.html#customization). Apart from allowing to add debug visualizers for library types without editing the source, I hoped to also have LDC support this feature even though it omits class and struct members in the debug info. Here's a tiny example:

struct String
{
    string _s;
}
string __debugOverview(const String* s)
{
    return s._s;
}

Unfortunately, LDC omits the function parameters from the function type, but they are needed for overload resolution. This a small patch to add these. It also ticks off one item of #1716 as the call stack now displays parameter types and values.

While mago supports passing the argument as a pointer or a reference, the latter doesn't work with LDC as the ref argument is typed as value. Is this removed somewhere in LDC if the ABI passes the value by reference anyway?

gen/dibuilder.cpp Outdated Show resolved Hide resolved
gen/dibuilder.cpp Outdated Show resolved Hide resolved
gen/dibuilder.cpp Outdated Show resolved Hide resolved
auto ditype = CreateTypeDescription(arg->type);
if (arg->byref) {
ditype = DBuilder.createPointerType(ditype, target.ptrsize * 8, 0, llvm::None,
processDIName((type->toPrettyChars(true) + llvm::StringRef("*")).str()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arg->byref might be set for non-D-refs. I think the only robust way would be to use IrFuncTyArg::parametersIdx and look up the AST param.

Also note that there's a DBuilder.createReferenceType(); used in e.g. DIBuilder::EmitLocalVariable(). But a struct this needs to be represented as a pointer, not a ref, see comment in that function...

Copy link
Member

@kinke kinke Dec 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arg->byref might be set for non-D-refs. I think the only robust way would be to use IrFuncTyArg::parametersIdx and look up the AST param.

It's totally unclear to me what these new param DIs are used for (as inside a function, we do have DIs for the params, but for their variable storage in memory, just like for locals, so not tied to the raw IR param). Maybe IrFuncTyArg::byref is better than the frontend ref-ness indeed - with a likely exception for byval params though (IrFuncTyArg::isByVal()) I guess.

Edit: Probably best to check what clang emits (in IR).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting: https://cpp.godbolt.org/z/x83nbd4Eh

; Large foo(Large l, Large &, NonPOD) { return l; }
define linkonce_odr dso_local void @S::foo(Large, Large&, NonPOD)(ptr dead_on_unwind noalias writable sret(%struct.Large) align 4 %agg.result, ptr noundef nonnull align 1 dereferenceable(1) %this, ptr noundef byval(%struct.Large) align 8 %l, ptr noundef nonnull align 4 dereferenceable(32) %0, ptr noundef %1) comdat align 2 !dbg !46 {

…

!46 = distinct !DISubprogram(name: "foo", linkageName: "S::foo(Large, Large&, NonPOD)", scope: !25, file: !4, line: 11, type: !28, scopeLine: 11, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, declaration: !27, retainedNodes: !23)

!28 = !DISubroutineType(types: !29)
!29 = !{!30, !36, !30, !37, !3}

!3 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "NonPOD", file: !4, line: 5, size: 32, flags: DIFlagTypePassByReference | DIFlagNonTrivial, elements: !5, identifier: "_ZTS6NonPOD")
!30 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Large", file: !4, line: 1, size: 256, flags: DIFlagTypePassByValue, elements: !31, identifier: "_ZTS5Large")
!36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
!37 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !30, size: 64)

So clang uses the frontend signature and types, only adding the implicit this pointer (with extra flags) - no sret pointer. And the types and ref-ness is determined from the AST alone as well - the IR function has 5 pointer IR params, but the function-type-DI only has 4 - implicit this (pointer), Large l (value), Large& (ref) and NonPOD (value).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've had a go at reworking this accordingly and pushed a commit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @kinke , that makes it a bit simpler again, i.e. no dependency on the IR. It doesn't show some hidden parameters (e.g. _arguments), but I'm not sure if that's good or bad.

The new version works with mago, too. I had some troubles also getting the struct members to work, because

  • sometimes the struct type is returned when querying IDiaSymbol instead of a pointer to the struct.
  • there seem to be conflicting debug information regarding the calling convention depending on how the symbol is accessed.

I don't see how this is caused by the emitted debug symbols, as it looks correct in a dump of the debug symbols. I have added some workarounds to mago.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's totally unclear to me what these new param DIs are used for

The VS debugger seems to need this information to populate the parameter types and values in the call stack.

Mago uses the function type to find the correct overload of the __debug* functions and verifies the signature so that it can call the fucntion in the debugged process. Indeed, this could also check debug info for local variables (not sure you can easily distinguish between function arguments and locals), but it seems more complicated.

Copy link
Member

@kinke kinke Dec 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @kinke , that makes it a bit simpler again, i.e. no dependency on the IR.

Yeah as I brought you on the wrong (IR) track, I figured I better 'fix' it again. :)

It doesn't show some hidden parameters (e.g. _arguments), but I'm not sure if that's good or bad.

Same here.

The new version works with mago, too. I had some troubles also getting the struct members to work, because

* sometimes the struct type is returned when querying IDiaSymbol instead of a pointer to the struct.

* there seem to be conflicting debug information regarding the calling convention depending on how the symbol is accessed.

I don't see how this is caused by the emitted debug symbols, as it looks correct in a dump of the debug symbols. I have added some workarounds to mago.

clang uses an abundance of flags (DIFlagTypePassByReference | DIFlagNonTrivial, DIFlagTypePassByValue), that might be required/helpful in some circumstances. And I haven't quickly found a way to add the extra 'this' pointer flags, so I left that as a TODO comment.

gen/dibuilder.cpp Outdated Show resolved Hide resolved
gen/dibuilder.cpp Outdated Show resolved Hide resolved
gen/dibuilder.cpp Outdated Show resolved Hide resolved
if (fd) {
Type *pointeeType = nullptr;
if (auto parentAggregate = fd->isThis()) {
pointeeType = parentAggregate->type;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad - this works for structs, but not for classes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix amended, incl. 'object-pointer' and 'artificial' flags.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks and sorry for the delay. The previous version already worked for me for classes, too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Np at all, my bad anyway, plus I didn't get to it sooner either. - FWIW, the previous version emitted an additional indirection for classes, so maybe you can get rid of a workaround in mago now.

@kinke kinke merged commit d607a27 into ldc-developers:master Jan 16, 2025
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants