vsk created this revision.
vsk added reviewers: aprantl, friss, jingham.

Split CallEdge into DirectCallEdge and IndirectCallEdge. Teach
DWARFExpression how to evaluate entry values in cases where the current
activation was created by an indirect call.

Writing tests for this is challenging, at the moment, because llvm emits
DW_AT_call_targets describing clobbered register values (llvm.org/PR43926).
I found a way to cover the non-tail call case, but would have to resort to
flaky assembly gadgets to test indirect tail-calls. I've left this for later.

rdar://57094085


https://reviews.llvm.org/D70100

Files:
  lldb/include/lldb/Symbol/Function.h
  lldb/include/lldb/Symbol/SymbolFile.h
  
lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp
  lldb/source/Expression/DWARFExpression.cpp
  lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
  lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
  lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
  lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
  lldb/source/Symbol/Function.cpp
  lldb/source/Target/StackFrameList.cpp

Index: lldb/source/Target/StackFrameList.cpp
===================================================================
--- lldb/source/Target/StackFrameList.cpp
+++ lldb/source/Target/StackFrameList.cpp
@@ -243,7 +243,8 @@
 /// \p return_pc) to \p end. On success this path is stored into \p path, and 
 /// on failure \p path is unchanged.
 static void FindInterveningFrames(Function &begin, Function &end,
-                                  Target &target, addr_t return_pc,
+                                  ExecutionContext &exe_ctx, Target &target,
+                                  addr_t return_pc,
                                   std::vector<Function *> &path,
                                   ModuleList &images, Log *log) {
   LLDB_LOG(log, "Finding frames between {0} and {1}, retn-pc={2:x}",
@@ -251,9 +252,9 @@
 
   // Find a non-tail calling edge with the correct return PC.
   if (log)
-    for (const CallEdge &edge : begin.GetCallEdges())
+    for (const auto &edge : begin.GetCallEdges())
       LLDB_LOG(log, "FindInterveningFrames: found call with retn-PC = {0:x}",
-               edge.GetReturnPCAddress(begin, target));
+               edge->GetReturnPCAddress(begin, target));
   CallEdge *first_edge = begin.GetCallEdgeForReturnAddress(return_pc, target);
   if (!first_edge) {
     LLDB_LOG(log, "No call edge outgoing from {0} with retn-PC == {1:x}",
@@ -262,7 +263,7 @@
   }
 
   // The first callee may not be resolved, or there may be nothing to fill in.
-  Function *first_callee = first_edge->GetCallee(images);
+  Function *first_callee = first_edge->GetCallee(images, exe_ctx);
   if (!first_callee) {
     LLDB_LOG(log, "Could not resolve callee");
     return;
@@ -283,8 +284,10 @@
     bool ambiguous = false;
     Function *end;
     ModuleList &images;
+    ExecutionContext &context;
 
-    DFS(Function *end, ModuleList &images) : end(end), images(images) {}
+    DFS(Function *end, ModuleList &images, ExecutionContext &context)
+        : end(end), images(images), context(context) {}
 
     void search(Function &first_callee, std::vector<Function *> &path) {
       dfs(first_callee);
@@ -313,8 +316,8 @@
 
       // Search the calls made from this callee.
       active_path.push_back(&callee);
-      for (CallEdge &edge : callee.GetTailCallingEdges()) {
-        Function *next_callee = edge.GetCallee(images);
+      for (const auto &edge : callee.GetTailCallingEdges()) {
+        Function *next_callee = edge->GetCallee(images, context);
         if (!next_callee)
           continue;
 
@@ -326,7 +329,7 @@
     }
   };
 
-  DFS(&end, images).search(*first_callee, path);
+  DFS(&end, images, exe_ctx).search(*first_callee, path);
 }
 
 /// Given that \p next_frame will be appended to the frame list, synthesize
@@ -379,8 +382,10 @@
   addr_t return_pc = next_reg_ctx_sp->GetPC();
   Target &target = *target_sp.get();
   ModuleList &images = next_frame.CalculateTarget()->GetImages();
-  FindInterveningFrames(*next_func, *prev_func, target, return_pc, path, images,
-                        log);
+  ExecutionContext exe_ctx(target_sp, /*get_process=*/true);
+  exe_ctx.SetFramePtr(&next_frame);
+  FindInterveningFrames(*next_func, *prev_func, exe_ctx, target, return_pc,
+                        path, images, log);
 
   // Push synthetic tail call frames.
   for (Function *callee : llvm::reverse(path)) {
Index: lldb/source/Symbol/Function.cpp
===================================================================
--- lldb/source/Symbol/Function.cpp
+++ lldb/source/Symbol/Function.cpp
@@ -17,6 +17,7 @@
 #include "lldb/Symbol/LineTable.h"
 #include "lldb/Symbol/SymbolFile.h"
 #include "lldb/Target/Language.h"
+#include "lldb/Target/Target.h"
 #include "lldb/Utility/Log.h"
 #include "llvm/Support/Casting.h"
 
@@ -127,23 +128,21 @@
   return FunctionInfo::MemorySize() + m_mangled.MemorySize();
 }
 
-//
-CallEdge::CallEdge(const char *symbol_name, lldb::addr_t return_pc,
-                   CallSiteParameterArray parameters)
-    : return_pc(return_pc), parameters(std::move(parameters)), resolved(false) {
-  lazy_callee.symbol_name = symbol_name;
-}
+/// @name Call site related structures
+/// @{
 
-llvm::ArrayRef<CallSiteParameter> CallEdge::GetCallSiteParameters() const {
-  return parameters;
+lldb::addr_t CallEdge::GetReturnPCAddress(Function &caller,
+                                          Target &target) const {
+  const Address &base = caller.GetAddressRange().GetBaseAddress();
+  return base.GetLoadAddress(&target) + return_pc;
 }
 
-void CallEdge::ParseSymbolFileAndResolve(ModuleList &images) {
+void DirectCallEdge::ParseSymbolFileAndResolve(ModuleList &images) {
   if (resolved)
     return;
 
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
-  LLDB_LOG(log, "CallEdge: Lazily parsing the call graph for {0}",
+  LLDB_LOG(log, "DirectCallEdge: Lazily parsing the call graph for {0}",
            lazy_callee.symbol_name);
 
   auto resolve_lazy_callee = [&]() -> Function * {
@@ -152,18 +151,19 @@
     images.FindFunctionSymbols(callee_name, eFunctionNameTypeAuto, sc_list);
     size_t num_matches = sc_list.GetSize();
     if (num_matches == 0 || !sc_list[0].symbol) {
-      LLDB_LOG(log, "CallEdge: Found no symbols for {0}, cannot resolve it",
+      LLDB_LOG(log,
+               "DirectCallEdge: Found no symbols for {0}, cannot resolve it",
                callee_name);
       return nullptr;
     }
     Address callee_addr = sc_list[0].symbol->GetAddress();
     if (!callee_addr.IsValid()) {
-      LLDB_LOG(log, "CallEdge: Invalid symbol address");
+      LLDB_LOG(log, "DirectCallEdge: Invalid symbol address");
       return nullptr;
     }
     Function *f = callee_addr.CalculateSymbolContextFunction();
     if (!f) {
-      LLDB_LOG(log, "CallEdge: Could not find complete function");
+      LLDB_LOG(log, "DirectCallEdge: Could not find complete function");
       return nullptr;
     }
     return f;
@@ -172,18 +172,50 @@
   resolved = true;
 }
 
-Function *CallEdge::GetCallee(ModuleList &images) {
+Function *DirectCallEdge::GetCallee(ModuleList &images, ExecutionContext &) {
   ParseSymbolFileAndResolve(images);
   assert(resolved && "Did not resolve lazy callee");
   return lazy_callee.def;
 }
 
-lldb::addr_t CallEdge::GetReturnPCAddress(Function &caller,
-                                          Target &target) const {
-  const Address &base = caller.GetAddressRange().GetBaseAddress();
-  return base.GetLoadAddress(&target) + return_pc;
+Function *IndirectCallEdge::GetCallee(ModuleList &images,
+                                      ExecutionContext &exe_ctx) {
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+  Status error;
+  Value callee_addr_val;
+  if (!call_target.Evaluate(&exe_ctx, exe_ctx.GetRegisterContext(),
+                            /*loclist_base_addr=*/LLDB_INVALID_ADDRESS,
+                            /*initial_value_ptr=*/nullptr,
+                            /*object_address_ptr=*/nullptr, callee_addr_val,
+                            &error)) {
+    LLDB_LOGF(log, "IndirectCallEdge: Could not evaluate expression: %s",
+              error.AsCString());
+    return nullptr;
+  }
+
+  addr_t raw_addr = callee_addr_val.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+  if (raw_addr == LLDB_INVALID_ADDRESS) {
+    LLDB_LOG(log, "IndirectCallEdge: Could not extract address from scalar");
+    return nullptr;
+  }
+
+  Address callee_addr;
+  if (!exe_ctx.GetTargetPtr()->ResolveLoadAddress(raw_addr, callee_addr)) {
+    LLDB_LOG(log, "IndirectCallEdge: Could not resolve callee's load address");
+    return nullptr;
+  }
+
+  Function *f = callee_addr.CalculateSymbolContextFunction();
+  if (!f) {
+    LLDB_LOG(log, "IndirectCallEdge: Could not find complete function");
+    return nullptr;
+  }
+
+  return f;
 }
 
+/// @}
+
 //
 Function::Function(CompileUnit *comp_unit, lldb::user_id_t func_uid,
                    lldb::user_id_t type_uid, const Mangled &mangled, Type *type,
@@ -246,7 +278,7 @@
   }
 }
 
-llvm::MutableArrayRef<CallEdge> Function::GetCallEdges() {
+llvm::ArrayRef<std::unique_ptr<CallEdge>> Function::GetCallEdges() {
   if (m_call_edges_resolved)
     return m_call_edges;
 
@@ -267,19 +299,20 @@
 
   // Sort the call edges to speed up return_pc lookups.
   llvm::sort(m_call_edges.begin(), m_call_edges.end(),
-             [](const CallEdge &LHS, const CallEdge &RHS) {
-               return LHS.GetUnresolvedReturnPCAddress() <
-                      RHS.GetUnresolvedReturnPCAddress();
+             [](const std::unique_ptr<CallEdge> &LHS,
+                const std::unique_ptr<CallEdge> &RHS) {
+               return LHS->GetUnresolvedReturnPCAddress() <
+                      RHS->GetUnresolvedReturnPCAddress();
              });
 
   return m_call_edges;
 }
 
-llvm::MutableArrayRef<CallEdge> Function::GetTailCallingEdges() {
+llvm::ArrayRef<std::unique_ptr<CallEdge>> Function::GetTailCallingEdges() {
   // Call edges are sorted by return PC, and tail calling edges have invalid
   // return PCs. Find them at the end of the list.
-  return GetCallEdges().drop_until([](const CallEdge &edge) {
-    return edge.GetUnresolvedReturnPCAddress() == LLDB_INVALID_ADDRESS;
+  return GetCallEdges().drop_until([](const std::unique_ptr<CallEdge> &edge) {
+    return edge->GetUnresolvedReturnPCAddress() == LLDB_INVALID_ADDRESS;
   });
 }
 
@@ -288,13 +321,13 @@
   auto edges = GetCallEdges();
   auto edge_it =
       std::lower_bound(edges.begin(), edges.end(), return_pc,
-                       [&](const CallEdge &edge, addr_t pc) {
-                         return edge.GetReturnPCAddress(*this, target) < pc;
+                       [&](const std::unique_ptr<CallEdge> &edge, addr_t pc) {
+                         return edge->GetReturnPCAddress(*this, target) < pc;
                        });
   if (edge_it == edges.end() ||
-      edge_it->GetReturnPCAddress(*this, target) != return_pc)
+      edge_it->get()->GetReturnPCAddress(*this, target) != return_pc)
     return nullptr;
-  return &const_cast<CallEdge &>(*edge_it);
+  return &const_cast<CallEdge &>(*edge_it->get());
 }
 
 Block &Function::GetBlock(bool can_create) {
Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
+++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
@@ -120,7 +120,7 @@
   void GetTypes(lldb_private::SymbolContextScope *sc_scope,
                 lldb::TypeClass type_mask,
                 lldb_private::TypeList &type_list) override;
-  std::vector<lldb_private::CallEdge>
+  std::vector<std::unique_ptr<lldb_private::CallEdge>>
   ParseCallEdgesInFunction(lldb_private::UserID func_id) override;
 
   void DumpClangAST(lldb_private::Stream &s) override;
Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -1071,7 +1071,7 @@
   }
 }
 
-std::vector<lldb_private::CallEdge>
+std::vector<std::unique_ptr<lldb_private::CallEdge>>
 SymbolFileDWARFDebugMap::ParseCallEdgesInFunction(UserID func_id) {
   uint32_t oso_idx = GetOSOIndexFromUserID(func_id.GetID());
   SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -292,7 +292,7 @@
   DIEInDeclContext(const lldb_private::CompilerDeclContext *parent_decl_ctx,
                    const DWARFDIE &die);
 
-  std::vector<lldb_private::CallEdge>
+  std::vector<std::unique_ptr<lldb_private::CallEdge>>
   ParseCallEdgesInFunction(UserID func_id) override;
 
   void Dump(lldb_private::Stream &s) override;
Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -3725,7 +3725,7 @@
 }
 
 /// Collect call graph edges present in a function DIE.
-static std::vector<lldb_private::CallEdge>
+static std::vector<std::unique_ptr<lldb_private::CallEdge>>
 CollectCallEdges(ModuleSP module, DWARFDIE function_die) {
   // Check if the function has a supported call site-related attribute.
   // TODO: In the future it may be worthwhile to support call_all_source_calls.
@@ -3743,32 +3743,85 @@
   // to be DWARF5-compliant. This may need to be done lazily to be performant.
   // For now, assume that all entries are nested directly under the subprogram
   // (this is the kind of DWARF LLVM produces) and parse them eagerly.
-  std::vector<CallEdge> call_edges;
+  std::vector<std::unique_ptr<CallEdge>> call_edges;
   for (DWARFDIE child = function_die.GetFirstChild(); child.IsValid();
        child = child.GetSibling()) {
     if (child.Tag() != DW_TAG_call_site)
       continue;
 
-    // Extract DW_AT_call_origin (the call target's DIE).
-    DWARFDIE call_origin = child.GetReferencedDIE(DW_AT_call_origin);
-    if (!call_origin.IsValid()) {
-      LLDB_LOG(log, "CollectCallEdges: Invalid call origin in {0}",
-               function_die.GetPubname());
+    llvm::Optional<DWARFDIE> call_origin = {};
+    llvm::Optional<DWARFExpression> call_target = {};
+    addr_t return_pc = LLDB_INVALID_ADDRESS;
+
+    DWARFAttributes attributes;
+    const size_t num_attributes = child.GetAttributes(attributes);
+    for (size_t i = 0; i < num_attributes; ++i) {
+      DWARFFormValue form_value;
+      if (!attributes.ExtractFormValueAtIndex(i, form_value)) {
+        LLDB_LOG(log, "CollectCallEdges: Could not extract TAG_call_site form");
+        break;
+      }
+
+      dw_attr_t attr = attributes.AttributeAtIndex(i);
+
+      // Extract DW_AT_call_origin (the call target's DIE).
+      if (attr == DW_AT_call_origin) {
+        call_origin = form_value.Reference();
+        if (!call_origin->IsValid()) {
+          LLDB_LOG(log, "CollectCallEdges: Invalid call origin in {0}",
+                   function_die.GetPubname());
+          break;
+        }
+      }
+
+      // Extract DW_AT_call_return_pc (the PC the call returns to) if it's
+      // available. It should only ever be unavailable for tail call edges, in
+      // which case use LLDB_INVALID_ADDRESS.
+      if (attr == DW_AT_call_return_pc)
+        return_pc = form_value.Address();
+
+      // Extract DW_AT_call_target (the location of the address of the indirect
+      // call).
+      if (attr == DW_AT_call_target) {
+        if (!DWARFFormValue::IsBlockForm(form_value.Form())) {
+          LLDB_LOG(log,
+                   "CollectCallEdges: AT_call_target does not have block form");
+          break;
+        }
+
+        auto data = child.GetData();
+        uint32_t block_offset = form_value.BlockData() - data.GetDataStart();
+        uint32_t block_length = form_value.Unsigned();
+        call_target = DWARFExpression(
+            module, DataExtractor(data, block_offset, block_length),
+            child.GetCU());
+      }
+    }
+    if (!call_origin && !call_target) {
+      LLDB_LOG(log, "CollectCallEdges: call site without any call target");
       continue;
     }
 
-    // Extract DW_AT_call_return_pc (the PC the call returns to) if it's
-    // available. It should only ever be unavailable for tail call edges, in
-    // which case use LLDB_INVALID_ADDRESS.
-    addr_t return_pc = child.GetAttributeValueAsAddress(DW_AT_call_return_pc,
-                                                        LLDB_INVALID_ADDRESS);
-
     // Extract call site parameters.
     CallSiteParameterArray parameters =
         CollectCallSiteParameters(module, child);
 
-    LLDB_LOG(log, "CollectCallEdges: Found call origin: {0} (retn-PC: {1:x})",
-             call_origin.GetPubname(), return_pc);
+    std::unique_ptr<CallEdge> edge;
+    if (call_origin) {
+      LLDB_LOG(log, "CollectCallEdges: Found call origin: {0} (retn-PC: {1:x})",
+               call_origin->GetPubname(), return_pc);
+      edge = std::make_unique<DirectCallEdge>(call_origin->GetMangledName(),
+                                              return_pc, std::move(parameters));
+    } else {
+      StreamString call_target_desc;
+      call_target->GetDescription(&call_target_desc, eDescriptionLevelBrief,
+                                  LLDB_INVALID_ADDRESS, nullptr);
+      LLDB_LOG(log, "CollectCallEdges: Found indirect call target: {0}",
+               call_target_desc.GetString());
+      edge = std::make_unique<IndirectCallEdge>(*call_target, return_pc,
+                                                std::move(parameters));
+    }
+
     if (log && parameters.size()) {
       for (const CallSiteParameter &param : parameters) {
         StreamString callee_loc_desc, caller_loc_desc;
@@ -3783,13 +3836,12 @@
       }
     }
 
-    call_edges.emplace_back(call_origin.GetMangledName(), return_pc,
-                            std::move(parameters));
+    call_edges.push_back(std::move(edge));
   }
   return call_edges;
 }
 
-std::vector<lldb_private::CallEdge>
+std::vector<std::unique_ptr<lldb_private::CallEdge>>
 SymbolFileDWARF::ParseCallEdgesInFunction(UserID func_id) {
   DWARFDIE func_die = GetDIE(func_id.GetID());
   if (func_die.IsValid())
Index: lldb/source/Expression/DWARFExpression.cpp
===================================================================
--- lldb/source/Expression/DWARFExpression.cpp
+++ lldb/source/Expression/DWARFExpression.cpp
@@ -830,6 +830,8 @@
 
   CallEdge *call_edge = nullptr;
   ModuleList &modlist = target.GetImages();
+  ExecutionContext parent_exe_ctx = *exe_ctx;
+  parent_exe_ctx.SetFrameSP(parent_frame);
   if (!parent_frame->IsArtificial()) {
     // If the parent frame is not artificial, the current activation may be
     // produced by an ambiguous tail call. In this case, refuse to proceed.
@@ -841,7 +843,7 @@
                return_pc, parent_func->GetName());
       return false;
     }
-    Function *callee_func = call_edge->GetCallee(modlist);
+    Function *callee_func = call_edge->GetCallee(modlist, parent_exe_ctx);
     if (callee_func != current_func) {
       LLDB_LOG(log, "Evaluate_DW_OP_entry_value: ambiguous call sequence, "
                     "can't find real parent frame");
@@ -851,9 +853,9 @@
     // The StackFrameList solver machinery has deduced that an unambiguous tail
     // call sequence that produced the current activation.  The first edge in
     // the parent that points to the current function must be valid.
-    for (CallEdge &edge : parent_func->GetTailCallingEdges()) {
-      if (edge.GetCallee(modlist) == current_func) {
-        call_edge = &edge;
+    for (auto &edge : parent_func->GetTailCallingEdges()) {
+      if (edge->GetCallee(modlist, parent_exe_ctx) == current_func) {
+        call_edge = edge.get();
         break;
       }
     }
@@ -907,8 +909,6 @@
   // TODO: Add support for DW_OP_push_object_address within a DW_OP_entry_value
   // subexpresion whenever llvm does.
   Value result;
-  ExecutionContext parent_exe_ctx = *exe_ctx;
-  parent_exe_ctx.SetFrameSP(parent_frame);
   const DWARFExpression &param_expr = matched_param->LocationInCaller;
   if (!param_expr.Evaluate(&parent_exe_ctx,
                            parent_frame->GetRegisterContext().get(),
Index: lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp
===================================================================
--- lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp
+++ lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp
@@ -140,6 +140,34 @@
   func11_tailcalled(sink, x);
 }
 
+__attribute__((noinline))
+void func13(int &sink, int x) {
+  //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC13-BT")
+  // FUNC13-BT: func13{{.*}}
+  // FUNC13-BT-NEXT: func14{{.*}}
+  use(x);
+
+  // Destroy 'x' in the current frame.
+  DESTROY_RSI;
+
+  //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC13-EXPR")
+  // FUNC13-EXPR: (int) ${{.*}} = 123
+
+  ++sink;
+}
+
+__attribute__((noinline, disable_tail_calls))
+void func14(int &sink, void (*target_no_tailcall)(int &, int)) {
+  // Move the call target into a register that won't get clobbered. Do this
+  // by calling the same indirect target twice, and hoping that regalloc is
+  // 'smart' enough to stash the call target in a non-clobbered register.
+  //
+  // llvm.org/PR43926 tracks work in the compiler to emit call targets which
+  // describe non-clobbered values.
+  target_no_tailcall(sink, 123);
+  target_no_tailcall(sink, 123);
+}
+
 __attribute__((disable_tail_calls))
 int main() {
   int sink = 0;
@@ -168,5 +196,8 @@
   // Test that evaluation can "see through" tail calls.
   func12(sink, 123);
 
+  // Test that evaluation can "see through" an indirect tail call.
+  func14(sink, func13);
+
   return 0;
 }
Index: lldb/include/lldb/Symbol/SymbolFile.h
===================================================================
--- lldb/include/lldb/Symbol/SymbolFile.h
+++ lldb/include/lldb/Symbol/SymbolFile.h
@@ -220,7 +220,8 @@
   const ObjectFile *GetObjectFile() const { return m_objfile_sp.get(); }
   ObjectFile *GetMainObjectFile();
 
-  virtual std::vector<CallEdge> ParseCallEdgesInFunction(UserID func_id) {
+  virtual std::vector<std::unique_ptr<CallEdge>>
+  ParseCallEdgesInFunction(UserID func_id) {
     return {};
   }
 
Index: lldb/include/lldb/Symbol/Function.h
===================================================================
--- lldb/include/lldb/Symbol/Function.h
+++ lldb/include/lldb/Symbol/Function.h
@@ -19,6 +19,8 @@
 
 namespace lldb_private {
 
+class ExecutionContext;
+
 /// \class FunctionInfo Function.h "lldb/Symbol/Function.h"
 /// A class that contains generic function information.
 ///
@@ -264,23 +266,14 @@
 /// in the call graph between two functions, or to evaluate DW_OP_entry_value.
 class CallEdge {
 public:
-  /// Construct a call edge using a symbol name to identify the calling
-  /// function, and a return PC within the calling function to identify a
-  /// specific call site.
-  ///
-  /// TODO: A symbol name may not be globally unique. To disambiguate ODR
-  /// conflicts, it's necessary to determine the \c Target a call edge is
-  /// associated with before resolving it.
-  CallEdge(const char *symbol_name, lldb::addr_t return_pc,
-           CallSiteParameterArray parameters);
-
-  CallEdge(CallEdge &&) = default;
-  CallEdge &operator=(CallEdge &&) = default;
+  virtual ~CallEdge() {}
 
   /// Get the callee's definition.
   ///
-  /// Note that this might lazily invoke the DWARF parser.
-  Function *GetCallee(ModuleList &images);
+  /// Note that this might lazily invoke the DWARF parser. A register context
+  /// from the caller's activation is needed to find indirect call targets.
+  virtual Function *GetCallee(ModuleList &images,
+                              ExecutionContext &exe_ctx) = 0;
 
   /// Get the load PC address of the instruction which executes after the call
   /// returns. Returns LLDB_INVALID_ADDRESS iff this is a tail call. \p caller
@@ -293,17 +286,13 @@
   lldb::addr_t GetUnresolvedReturnPCAddress() const { return return_pc; }
 
   /// Get the call site parameters available at this call edge.
-  llvm::ArrayRef<CallSiteParameter> GetCallSiteParameters() const;
+  llvm::ArrayRef<CallSiteParameter> GetCallSiteParameters() const {
+    return parameters;
+  }
 
-private:
-  void ParseSymbolFileAndResolve(ModuleList &images);
-
-  /// Either the callee's mangled name or its definition, discriminated by
-  /// \ref resolved.
-  union {
-    const char *symbol_name;
-    Function *def;
-  } lazy_callee;
+protected:
+  CallEdge(lldb::addr_t return_pc, CallSiteParameterArray &&parameters)
+      : return_pc(return_pc), parameters(std::move(parameters)) {}
 
   /// An invalid address if this is a tail call. Otherwise, the function-local
   /// PC offset. Adding this PC offset to the function's base load address
@@ -311,11 +300,54 @@
   lldb::addr_t return_pc;
 
   CallSiteParameterArray parameters;
+};
+
+/// A direct call site.
+class DirectCallEdge : public CallEdge {
+public:
+  /// Construct a call edge using a symbol name to identify the callee, and a
+  /// return PC within the calling function to identify a specific call site.
+  DirectCallEdge(const char *symbol_name, lldb::addr_t return_pc,
+                 CallSiteParameterArray &&parameters)
+      : CallEdge(return_pc, std::move(parameters)) {
+    lazy_callee.symbol_name = symbol_name;
+  }
+
+  Function *GetCallee(ModuleList &images, ExecutionContext &exe_ctx) override;
+
+private:
+  void ParseSymbolFileAndResolve(ModuleList &images);
+
+  // Used to describe a direct call.
+  //
+  // Either the callee's mangled name or its definition, discriminated by
+  // \ref resolved.
+  union {
+    const char *symbol_name;
+    Function *def;
+  } lazy_callee;
 
   /// Whether or not an attempt was made to find the callee's definition.
-  bool resolved;
+  bool resolved = false;
+};
 
-  DISALLOW_COPY_AND_ASSIGN(CallEdge);
+/// An indirect call site.
+class IndirectCallEdge : public CallEdge {
+public:
+  /// Construct a call edge using a DWARFExpression to identify the callee, and
+  /// a return PC within the calling function to identify a specific call site.
+  IndirectCallEdge(DWARFExpression call_target, lldb::addr_t return_pc,
+                   CallSiteParameterArray &&parameters)
+      : CallEdge(return_pc, std::move(parameters)),
+        call_target(std::move(call_target)) {}
+
+  Function *GetCallee(ModuleList &images, ExecutionContext &exe_ctx) override;
+
+private:
+  // Used to describe an indirect call.
+  //
+  // Specifies the location of the callee address in the calling frame.
+  DWARFExpression call_target;
 };
 
 /// \class Function Function.h "lldb/Symbol/Function.h"
@@ -414,11 +446,11 @@
 
   /// Get the outgoing call edges from this function, sorted by their return
   /// PC addresses (in increasing order).
-  llvm::MutableArrayRef<CallEdge> GetCallEdges();
+  llvm::ArrayRef<std::unique_ptr<CallEdge>> GetCallEdges();
 
   /// Get the outgoing tail-calling edges from this function. If none exist,
   /// return None.
-  llvm::MutableArrayRef<CallEdge> GetTailCallingEdges();
+  llvm::ArrayRef<std::unique_ptr<CallEdge>> GetTailCallingEdges();
 
   /// Get the outgoing call edge from this function which has the given return
   /// address \p return_pc, or return nullptr. Note that this will not return a
@@ -587,11 +619,9 @@
   uint32_t
       m_prologue_byte_size; ///< Compute the prologue size once and cache it
 
-  // TODO: Use a layer of indirection to point to call edges, to save space
-  // when call info hasn't been parsed.
   bool m_call_edges_resolved = false; ///< Whether call site info has been
                                       ///  parsed.
-  std::vector<CallEdge> m_call_edges; ///< Outgoing call edges.
+  std::vector<std::unique_ptr<CallEdge>> m_call_edges; ///< Outgoing call edges.
 private:
   DISALLOW_COPY_AND_ASSIGN(Function);
 };
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to