This is an archive of the discontinued LLVM Phabricator instance.

[DebugInfo] Fix scope for local static variables
AbandonedPublic

Authored by krisb on Sep 13 2021, 9:37 AM.

Details

Summary

This fixes https://bugs.llvm.org/show_bug.cgi?id=44695.

On clang side we need to place static locals defined within a bracketed
block into a correct scope. Since getContextDescriptor() does no access lexical
blocks, just pick the innermost lexical scope early.

On llvm side the patch proposes to delay emission of static locals until their
parent scopes are created (just like normal local variables).
In case of inlined function, static locals are created only for abstract
entities (as per suggestions from https://bugs.llvm.org/show_bug.cgi?id=30637).

Diff Detail

Event Timeline

krisb created this revision.Sep 13 2021, 9:37 AM
krisb requested review of this revision.Sep 13 2021, 9:37 AM
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptSep 13 2021, 9:37 AM
ellis added a subscriber: ellis.Sep 13 2021, 10:13 AM

Thanks for working on this! getOrCreateGlobalVariableDIE() is also called in constructImportedEntityDIE() which means that there could be some globals without DIE parents. In D108492 I've added this to the added test case.

!18 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !4, entity: !19, line: 122)
!19 = !DIGlobalVariable(name: "imported_static_var", scope: !6, file: !5, line: 3, type: !10, isLocal: false, isDefinition: true)

But maybe we can fix this in another diff?

krisb added a comment.Sep 13 2021, 1:02 PM

I've added this to the added test case.

!18 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !4, entity: !19, line: 122)
!19 = !DIGlobalVariable(name: "imported_static_var", scope: !6, file: !5, line: 3, type: !10, isLocal: false, isDefinition: true)

Thank you for pointing to this! Just in case, do you have a C++ example, that reproduces the issue with imported static local declaration? I'm asking, cause in your example DW_TAG_imported_declaration has CU as a scope, while DIGlobalVariable is function-scoped. This looks a bit strange. I mean, constructImportedEntityDIE() called from DwarfDebug::beginModule() skips all local-scoped entities, and unless I'm missing something static locals shouldn't go here. Another call for constructImportedEntityDIE() is from DwarfCompileUnit::createScopeChildrenDIE where I'd not expect any issue with parents.

But it seems imported declarations are broken not just for static locals, but for all inlined entities, for example

namespace ns {
inline __attribute__((always_inline))
int foo() {
  int a = 42; 
  return a;
}
}

int main() {
  using ns::foo;
  return foo();
}

produces (with or w/o this patch) imported declaration for foo() that refers to an empty subprogram:

x0000002a:   DW_TAG_namespace
                DW_AT_name  ("ns")

0x0000002f:     DW_TAG_subprogram
                  DW_AT_name  ("foo")
                  DW_AT_inline  (DW_INL_inlined)

0x0000003f:       DW_TAG_variable
                    DW_AT_name  ("a")

0x0000004a:       NULL

0x0000004b:     DW_TAG_subprogram

0x0000004c:     NULL

0x00000054:   DW_TAG_subprogram
                DW_AT_name  ("main")

0x0000006d:     DW_TAG_imported_declaration
                  DW_AT_import  (0x0000004b)

while it should point to 0x0000002f.

ellis added a comment.Sep 13 2021, 5:30 PM

I've added this to the added test case.

!18 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !4, entity: !19, line: 122)
!19 = !DIGlobalVariable(name: "imported_static_var", scope: !6, file: !5, line: 3, type: !10, isLocal: false, isDefinition: true)

Thank you for pointing to this! Just in case, do you have a C++ example, that reproduces the issue with imported static local declaration? I'm asking, cause in your example DW_TAG_imported_declaration has CU as a scope, while DIGlobalVariable is function-scoped. This looks a bit strange. I mean, constructImportedEntityDIE() called from DwarfDebug::beginModule() skips all local-scoped entities, and unless I'm missing something static locals shouldn't go here. Another call for constructImportedEntityDIE() is from DwarfCompileUnit::createScopeChildrenDIE where I'd not expect any issue with parents.

No, I designed the test to hit that code path, but I should have found a C++ example to back it up. Thanks for looking into this!

ellis added a comment.Sep 22 2021, 3:27 PM

But it seems imported declarations are broken not just for static locals, but for all inlined entities, for example

namespace ns {
inline __attribute__((always_inline))
int foo() {
  int a = 42; 
  return a;
}
}

int main() {
  using ns::foo;
  return foo();
}

produces (with or w/o this patch) imported declaration for foo() that refers to an empty subprogram:

x0000002a:   DW_TAG_namespace
                DW_AT_name  ("ns")

0x0000002f:     DW_TAG_subprogram
                  DW_AT_name  ("foo")
                  DW_AT_inline  (DW_INL_inlined)

0x0000003f:       DW_TAG_variable
                    DW_AT_name  ("a")

0x0000004a:       NULL

0x0000004b:     DW_TAG_subprogram

0x0000004c:     NULL

0x00000054:   DW_TAG_subprogram
                DW_AT_name  ("main")

0x0000006d:     DW_TAG_imported_declaration
                  DW_AT_import  (0x0000004b)

while it should point to 0x0000002f.

I've looked into this and realized that clang correctly emits a DW_TAG_inlined_subroutine for foo so the variables here are actually ok. The DW_TAG_imported_declaration tag is incorrect though, and I have a fix in https://reviews.llvm.org/D110294.

There have been a few different patches going around related to mis-scoped things - imported entities and types, maybe other things too? (static variables? Or has that already been fixed now) Perhaps some summary of the state of all these patches sent to llvm-dev/linked from the various reviews would be helpful? It'd be good to know if/how all these patches are hopefully aiming for some unified solution.

krisb added a comment.EditedSep 23 2021, 11:23 AM

@dblaikie yeah, the problem(s) seemed easier and smaller :(

Basically, we have two issues with local scopes here:
(1) function-scoped entities like static variables, type definitions/typedefs, etc have incorrect (empty) parent DIE if the function containing them was inlined. We do not consider abstract SP as a possible parent DIE and try to create a new DIE for a function that doesn't exist. @ellis is working on this issue in [0] (for static vars) and [1] (for imported declarations).
(2) the same entities (static local vars, typedefs, etc) that should be scoped within a lexical block have a subprogram scope (in debug metadata) and parent DIE (in DWARF) instead. This is the issue I'm trying to fix in this patch, but for static variables only.

As a side effect, this patch fixes the issue with inlined functions for static vars (1) as well. But it seems the issues are not related and can be fixed separately.
And as now I've realized that static locals is not the only problem, this patch should implement a more generic solution to cover other entities. So, please, consider it as a WIP.

[0] https://reviews.llvm.org/D108492
[1] https://reviews.llvm.org/D110294

krisb planned changes to this revision.Sep 23 2021, 11:24 AM

@dblaikie yeah, the problem(s) seemed easier and smaller :(

Basically, we have two issues with local scopes here:
(1) function-scoped entities like static variables, type definitions/typedefs, etc have incorrect (empty) parent DIE if the function containing them was inlined. We do not consider abstract SP as a possible parent DIE and try to create a new DIE for a function that doesn't exist. @ellis is working on this issue in [0] (for static vars) and [1] (for imported declarations).
(2) the same entities (static local vars, typedefs, etc) that should be scoped within a lexical block have a subprogram scope (in debug metadata) and parent DIE (in DWARF) instead. This is the issue I'm trying to fix in this patch, but for static variables only.

As a side effect, this patch fixes the issue with inlined functions for static vars (1) as well. But it seems the issues are not related and can be fixed separately.
And as now I've realized that static locals is not the only problem, this patch should implement a more generic solution to cover other entities. So, please, consider it as a WIP.

Hey @krisb I was under the impression that this patch would fix the static variable bug. Should I wait to see what this patch fixes before working on [0] and [1]?

krisb added a comment.Sep 30 2021, 4:57 AM

@ellis I'd appreciate if you go ahead and fix the issues with inlined functions. Sorry for interrupting you in D108492.

krisb edited the summary of this revision. (Show Details)Sep 30 2021, 4:57 AM