This is an archive of the discontinued LLVM Phabricator instance.

[DWARF] Fix lookup in the abstract origins of inlined blocks/functions
ClosedPublic

Authored by spyffe on Apr 21 2017, 2:34 PM.

Details

Summary

LLDB uses clang::DeclContexts for lookups, and variables get put into the DeclContext for their abstract origin. (The abstract origin is a DWARF pointer that indicates the unique definition of inlined code.) When the expression parser is looking for variables, it locates the DeclContext for the current context. This needs to be done carefully, though, e.g.:

__attribute__ ((always_inline)) void f(int a) {
  {
    int b = a * 2;
  }
}

void g() {
  f(3);
}

Here, if we're stopped in the inlined copy of f, we have to find the DeclContext corresponding to the definition of f – its abstract origin. Clang doesn't allow multiple functions with the same name and arguments to exist. It also means that any variables we see must be placed in the appropriate DeclContext.

[Bug 1]: When stopped in an inline block, the function GetDeclContextDIEContainingDIE for that block doesn't properly construct a DeclContext for the abstract origin for inlined subroutines. That means we get duplicated function DeclContexts, but function arguments only get put in the abstract origin's DeclContext, and as a result when we try to look for them in nested contexts they aren't found.

[Bug 2]: When stopped in an inline block, the DWARF (for space reasons) doesn't explicitly point to the abstract origin for that block. This means that the function GetClangDeclContextForDIE returns a different DeclContext for each place the block is inlined. However, any variables defined in the block have abstract origins, so they will only get placed in the DeclContext for their abstract origin.

In this fix, I've introduced a test covering both of these issues, and fixed them.

Bug 1 could be resolved simply by making sure we look up the abstract origin for inlined functions when looking up their DeclContexts on behalf of nested blocks.

For Bug 2, I've implemented an algorithm that makes the DeclContext for a block be the containing DeclContext for the closest entity we would find during lookup that has an abstract origin pointer. That means that in the following situation:

{ // block 1
  int a;
  { // block 2
    int b;
  }
}

if we looked up the DeclContext for block 2, we'd find the block containing the abstract origin of b, and lookup would proceed correctly because we'd see b and a. However, in the situation

{ // block 1
  int a;
  { // block 2
  }
}

since there isn't anything to look up in block 2, we can't determine its abstract origin (and there is no such pointer in the DWARF for blocks). However, we can walk up the parent chain and find a, and its abstract origin lives in the abstract origin of block 1. So we simply say that the DeclContext for block 2 is the same as the DeclContext for block 1, which contains a. Lookups will return the same results.

Diff Detail

Repository
rL LLVM

Event Timeline

spyffe created this revision.Apr 21 2017, 2:34 PM
jingham edited edge metadata.Apr 24 2017, 12:09 PM

Except for the comment about FindFirstChildWithAbstractOrigin, this seems fine.

source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
3723–3742 ↗(On Diff #96229)

The second of these functions seems dangerous since it doesn't check that block is contained in function. As such, I'm not sure it's worth breaking it out into a separate function rather than inlining it in GetDeclContextForBlock, where that relationship is already known.

spyffe added inline comments.Apr 24 2017, 1:57 PM
source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
3723–3742 ↗(On Diff #96229)

I'll put in an assert() that context is a DW_TAG_subprogram or DW_TAG_inlined_subroutine iff it is == function to make sure people use it right.

This revision was automatically updated to reflect the committed changes.