[DWARF] Fix lookup in the abstract origins of inlined blocks/functions
authorSean Callanan <scallanan@apple.com>
Mon, 24 Apr 2017 22:11:10 +0000 (22:11 +0000)
committerSean Callanan <scallanan@apple.com>
Mon, 24 Apr 2017 22:11:10 +0000 (22:11 +0000)
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.

Thanks to Jim Ingham for review and suggestions.

Differential revision: https://reviews.llvm.org/D32375

llvm-svn: 301263

lldb/packages/Python/lldbsuite/test/lang/c/inlines/main.c
lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp

index e9bd894..f45e4e2 100644 (file)
@@ -5,6 +5,11 @@ inline void test2(int) __attribute__ ((always_inline));
 
 void test2(int b) {
     printf("test2(%d)\n", b); //% self.expect("expression b", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["42"])
+    {
+      int c = b * 2;
+      printf("c=%d\n", c); //% self.expect("expression b", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["42"])
+                           //% self.expect("expression c", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["84"])
+    }
 }
 
 void test1(int a) {
index e2902d5..1d8b759 100644 (file)
@@ -3682,7 +3682,7 @@ DWARFASTParserClang::GetClangDeclContextForDIE(const DWARFDIE &die) {
       break;
 
     case DW_TAG_lexical_block:
-      decl_ctx = (clang::DeclContext *)ResolveBlockDIE(die);
+      decl_ctx = GetDeclContextForBlock(die);
       try_parsing_type = false;
       break;
 
@@ -3704,6 +3704,69 @@ DWARFASTParserClang::GetClangDeclContextForDIE(const DWARFDIE &die) {
   return nullptr;
 }
 
+static bool IsSubroutine(const DWARFDIE &die) {
+  switch (die.Tag()) {
+  case DW_TAG_subprogram:
+  case DW_TAG_inlined_subroutine:
+    return true;
+  default:
+    return false;
+  }
+}
+
+static DWARFDIE GetContainingFunctionWithAbstractOrigin(const DWARFDIE &die) {
+  for (DWARFDIE candidate = die; candidate; candidate = candidate.GetParent()) {
+    if (IsSubroutine(candidate)) {
+      if (candidate.GetReferencedDIE(DW_AT_abstract_origin)) {
+        return candidate;
+      } else {
+        return DWARFDIE();
+      }
+    }
+  }
+  assert(!"Shouldn't call GetContainingFunctionWithAbstractOrigin on something "
+          "not in a function");
+  return DWARFDIE();
+}
+
+static DWARFDIE FindAnyChildWithAbstractOrigin(const DWARFDIE &context) {
+  for (DWARFDIE candidate = context.GetFirstChild(); candidate.IsValid();
+       candidate = candidate.GetSibling()) {
+    if (candidate.GetReferencedDIE(DW_AT_abstract_origin)) {
+      return candidate;
+    }
+  }
+  return DWARFDIE();
+}
+
+static DWARFDIE FindFirstChildWithAbstractOrigin(const DWARFDIE &block,
+                                                 const DWARFDIE &function) {
+  assert(IsSubroutine(function));
+  for (DWARFDIE context = block; context != function.GetParent();
+       context = context.GetParent()) {
+    assert(!IsSubroutine(context) || context == function);
+    if (DWARFDIE child = FindAnyChildWithAbstractOrigin(context)) {
+      return child;
+    }
+  }
+  return DWARFDIE();
+}
+
+clang::DeclContext *
+DWARFASTParserClang::GetDeclContextForBlock(const DWARFDIE &die) {
+  assert(die.Tag() == DW_TAG_lexical_block);
+  DWARFDIE containing_function_with_abstract_origin =
+      GetContainingFunctionWithAbstractOrigin(die);
+  if (!containing_function_with_abstract_origin) {
+    return (clang::DeclContext *)ResolveBlockDIE(die);
+  }
+  DWARFDIE child = FindFirstChildWithAbstractOrigin(
+      die, containing_function_with_abstract_origin);
+  CompilerDeclContext decl_context =
+      GetDeclContextContainingUIDFromDWARF(child);
+  return (clang::DeclContext *)decl_context.GetOpaqueDeclContext();
+}
+
 clang::BlockDecl *DWARFASTParserClang::ResolveBlockDIE(const DWARFDIE &die) {
   if (die && die.Tag() == DW_TAG_lexical_block) {
     clang::BlockDecl *decl =
index 3d6d08e..57c1fc0 100644 (file)
@@ -66,6 +66,8 @@ protected:
   class DelayedAddObjCClassProperty;
   typedef std::vector<DelayedAddObjCClassProperty> DelayedPropertyList;
 
+  clang::DeclContext *GetDeclContextForBlock(const DWARFDIE &die);
+
   clang::BlockDecl *ResolveBlockDIE(const DWARFDIE &die);
 
   clang::NamespaceDecl *ResolveNamespaceDIE(const DWARFDIE &die);
index 856c371..973b5ef 100644 (file)
@@ -3051,7 +3051,13 @@ SymbolFileDWARF::GetDeclContextDIEContainingDIE(const DWARFDIE &orig_die) {
         case DW_TAG_lexical_block:
         case DW_TAG_subprogram:
           return die;
-
+        case DW_TAG_inlined_subroutine: {
+          DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin);
+          if (abs_die) {
+            return abs_die;
+          }
+          break;
+        }
         default:
           break;
         }